/* shared.css — block styles scoped under .rrw
   Ported from admin/public/rr-builder.html.
   Transformation: frame → .rrw, block-level classes scoped under .rrw,
   frame.mobile → .rrw.mobile, .mobile prefix → .rrw.mobile
   Do NOT redesign — faithful port only. */

/* ── Base frame ── */
.rrw{background:var(--c-bg);color:var(--c-text);font-family:var(--c-body);line-height:1.6;
  --c-bg:#FAF6F0;--c-bg-muted:#F0E8DC;--c-text:#2A1F18;--c-head:var(--c-text);--c-accent:#C8553D;--c-border:#E5DBC9;--c-on-accent:#fff;--c-heading:'Playfair Display',serif;--c-body:'Inter',sans-serif;--c-radius:8px}
.rrw.mobile{width:390px;border-radius:30px;box-shadow:0 30px 70px -18px rgba(6,4,38,.42),0 0 0 1px rgba(6,4,38,.06);margin:6px 0}
.rrw *{box-sizing:border-box}
.rrw h1,.rrw h2,.rrw h3,.rrw h4,.rrw .h{font-family:var(--c-heading);font-weight:600;line-height:1.1;letter-spacing:-.01em;color:var(--c-head)}

/* ── Primitives ── */
.rrw .cbtn{display:inline-block;padding:13px 26px;border-radius:var(--c-radius);background:var(--c-accent);color:var(--c-on-accent);font-weight:600;font-size:13px;letter-spacing:.02em;border:1.5px solid var(--c-accent);cursor:pointer;white-space:nowrap;text-decoration:none;transition:background .16s,color .16s,border-color .16s,filter .16s}
.rrw .cbtn:hover{filter:brightness(1.08)}
.rrw .cbtn.ghost{background:transparent;color:var(--c-accent)}
.rrw .cbtn.ghost:hover{background:var(--c-accent);color:var(--c-on-accent);filter:none}

/* button style variants */
.rrw.btn-solid-rounded .cbtn{border-radius:9999px}
.rrw.btn-solid-square .cbtn{border-radius:0}
.rrw.btn-solid-soft .cbtn{border-radius:8px}
.rrw.btn-outline-rounded .cbtn{border-radius:9999px}
.rrw.btn-outline-square .cbtn{border-radius:0}
.rrw.btn-outline-rounded .cbtn,.rrw.btn-outline-square .cbtn{background:transparent;color:var(--c-accent)}
.rrw.btn-ghost-underline .cbtn{background:transparent;color:var(--c-accent);border:none;border-bottom:2px solid var(--c-accent);border-radius:0;padding:8px 2px}
.rrw.btn-ghost-underline .cbtn:hover,.rrw.btn-ghost-underline .cbtn.ghost:hover{background:transparent;color:var(--c-accent);filter:none;opacity:.6}

/* ── Placeholder ── */
.rrw .ph{background:linear-gradient(135deg,color-mix(in oklab,var(--c-accent) 75%,#000),var(--c-text));background-size:cover;background-position:center;position:relative}
.rrw .ph::after{content:'';position:absolute;inset:0;background:radial-gradient(120% 80% at 30% 20%,rgba(255,255,255,.10),transparent 60%)}
.rrw .ph.img::after{background:rgba(0,0,0,.18)}

/* readable fade on hero images */
.rrw .hero.ph.img::after{background:linear-gradient(90deg,rgba(0,0,0,calc(var(--ho,1)*.62)),rgba(0,0,0,calc(var(--ho,1)*.34)) 45%,rgba(0,0,0,calc(var(--ho,1)*.06)) 80%),linear-gradient(0deg,rgba(0,0,0,calc(var(--ho,1)*.45)),rgba(0,0,0,calc(var(--ho,1)*.12)) 55%,transparent)}
.rrw .hero.ph.img[style*="text-align:center"]::after{background:linear-gradient(180deg,rgba(0,0,0,calc(var(--ho,1)*.32)),rgba(0,0,0,calc(var(--ho,1)*.22)) 50%,rgba(0,0,0,calc(var(--ho,1)*.5))),radial-gradient(120% 90% at 50% 38%,rgba(0,0,0,calc(var(--ho,1)*.18)),rgba(0,0,0,calc(var(--ho,1)*.42)))}

/* ── Marquee bands ── */
@keyframes rrmarq{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@keyframes rrmarq2{from{transform:translateX(-50%)}to{transform:translateX(0)}}
.rrw .marq{display:flex;gap:8px;width:max-content}
.rrw .marq>div{width:150px;height:96px;border-radius:8px;background-size:cover;background-position:center;flex-shrink:0}
.rrw .marq.a{animation:rrmarq 22s linear infinite}.rrw .marq.b{animation:rrmarq2 26s linear infinite;margin-top:8px}

/* ── Section layout ── */
.rrw .sec{padding:clamp(40px,5vw,72px) clamp(28px,5vw,56px)}
.rrw .eyebrow{font-size:11px;letter-spacing:.22em;text-transform:uppercase;color:var(--c-accent);font-weight:700;margin-bottom:12px}
.rrw .sec-h{font-size:clamp(26px,3.2vw,40px);color:var(--c-head);margin-bottom:10px}
.rrw .lead{max-width:560px;color:var(--c-text);opacity:.72;font-size:15px}
.rrw .grid3{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
.rrw.mobile .grid3{grid-template-columns:1fr}

/* ── Nav ── */
.rrw .nav{display:flex;align-items:center;padding:20px clamp(28px,5vw,56px);border-bottom:1px solid var(--c-border);background:var(--c-bg);gap:32px;position:relative}
.rrw .nav .logo{font-family:var(--c-heading);font-weight:700;font-size:20px;color:var(--c-text);letter-spacing:.05em;text-transform:uppercase;white-space:nowrap;display:inline-flex;align-items:center;gap:9px}
.rrw .nav .spacer{flex:1}
.rrw .nav .links{display:flex;gap:28px;font-size:13px;letter-spacing:.02em}
.rrw .nav .links a{color:var(--c-text);opacity:.7;text-decoration:none;position:relative;padding:3px 0;font-weight:500;cursor:pointer;transition:opacity .14s}
.rrw .nav .links a:hover{opacity:1}
.rrw .nav .links a::after{content:'';position:absolute;left:0;right:100%;bottom:-3px;height:1.5px;background:var(--c-accent);transition:right .22s}
.rrw .nav .links a:hover::after{right:0}
.rrw .nav-langtog{display:inline-flex;gap:5px;align-items:center;font-size:11px;font-weight:700;letter-spacing:.05em;margin:0 4px}
.rrw .nav-over{background:transparent;border-bottom:1px solid color-mix(in oklab,var(--c-border) 60%,transparent)}
.rrw .nav-bold .links{gap:30px}.rrw .nav-bold .links a{font-family:var(--c-heading);font-weight:700;font-size:15px;letter-spacing:.1em;text-transform:uppercase;opacity:.82}
.rrw.mobile .nav{gap:10px;padding:16px 18px}.rrw.mobile .nav .links,.rrw.mobile .nav .nav-langtog{display:none}
@media(max-width:760px){.rrw .nav .nav-langtog{display:none}.rrw .duohero{grid-template-columns:1fr!important}}
.rrw.mobile .duohero{grid-template-columns:1fr!important}
.rrw .nav .ham{font-size:19px;color:var(--c-text);cursor:pointer;line-height:1}
.rrw .nav .mob-only{display:none}
.rrw.mobile .nav .mob-only{display:inline-flex;align-items:center;font-size:21px;flex-shrink:0}
.rrw .navwrap{position:relative}
.rrw .navmenu{display:none;flex-direction:column;gap:2px;padding:10px clamp(20px,5vw,56px) 18px;background:var(--c-bg);border-bottom:1px solid var(--c-border)}
.rrw .navwrap.open .navmenu{display:flex}
.rrw .navmenu-links{display:flex;flex-direction:column}
.rrw .navmenu-links a{padding:11px 0;color:var(--c-text);opacity:.82;text-decoration:none;font-size:15px;font-weight:500;border-bottom:1px solid color-mix(in oklab,var(--c-border) 60%,transparent);cursor:pointer}
.rrw .navmenu-links a:hover{opacity:1}
.rrw .navmenu-cta{align-self:flex-start;margin-top:12px;padding:10px 22px;font-size:13px}
.rrw .ham{user-select:none}
.rrw .navwrap.open .ham{opacity:.55}

/* ── New navbar variants ── */

/* scroll-shrink: sticky bar that reduces padding + gains shadow on scroll */
.rrw .nav-scrollshrink{position:sticky;top:0;z-index:100;transition:padding .25s,box-shadow .25s,background .25s}
.rrw .nav-scrollshrink.scrolled{padding-top:10px;padding-bottom:10px;box-shadow:0 2px 12px rgba(0,0,0,.10);background:var(--c-bg)}

/* transparent-solid: transparent over hero, solid bg on scroll */
.rrw .nav-transparent-solid{position:sticky;top:0;z-index:100;background:transparent;border-bottom:1px solid rgba(255,255,255,.15);transition:background .25s,border-color .25s,box-shadow .25s,color .25s}
.rrw .nav-transparent-solid .logo{color:#fff}
.rrw .nav-transparent-solid .links a{color:#fff;opacity:.85}
.rrw .nav-transparent-solid .links a:hover{opacity:1}
.rrw .nav-transparent-solid .ham{color:#fff}
.rrw .nav-transparent-solid .nav-langtog{color:#fff}
.rrw .nav-transparent-solid.scrolled{background:var(--c-bg);border-bottom:1px solid var(--c-border);box-shadow:0 2px 12px rgba(0,0,0,.08)}
.rrw .nav-transparent-solid.scrolled .logo{color:var(--c-text)}
.rrw .nav-transparent-solid.scrolled .links a{color:var(--c-text);opacity:.7}
.rrw .nav-transparent-solid.scrolled .ham{color:var(--c-text)}
.rrw .nav-transparent-solid.scrolled .nav-langtog{color:var(--c-text)}

/* overlay-drawer: full-frame overlay menu opened by hamburger */
.rrw .nav-overlay-menu{position:absolute;inset:0;background:var(--c-bg);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:28px;z-index:200;opacity:0;pointer-events:none;transition:opacity .22s}
.rrw .navwrap.open .nav-overlay-menu{opacity:1;pointer-events:auto}
.rrw .nav-overlay-menu .links{flex-direction:column;align-items:center;gap:20px}
.rrw .nav-overlay-menu .links a{font-family:var(--c-heading);font-size:clamp(22px,4vw,36px);font-weight:700;opacity:.88;letter-spacing:.03em}
.rrw .nav-overlay-close{position:absolute;top:20px;right:clamp(20px,4vw,48px);font-size:22px;cursor:pointer;color:var(--c-text);opacity:.7;transition:opacity .14s;user-select:none}
.rrw .nav-overlay-close:hover{opacity:1}

/* slide-down: bar slides + fades in from top on mount (CSS animation, once) */
@keyframes nav-slide-down{from{transform:translateY(-100%);opacity:0}to{transform:translateY(0);opacity:1}}
.rrw .nav-slidedown{animation:nav-slide-down .5s cubic-bezier(.22,1,.36,1) both}

/* ── Mobile nav overrides ── */
.rrw.mobile .gallery-hero{gap:12px}
.rrw.mobile .gallery-hero .ph.img{min-height:300px!important}
.rrw.mobile .gallery-hero>div:last-child .ph.img{min-height:150px!important}
.rrw.mobile .nav .logo{font-size:15px;letter-spacing:.04em;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rrw.mobile .nav .cbtn{padding:8px 12px;font-size:11px;flex-shrink:0}

/* ── Mobile content overrides ── */
.rrw.mobile .hero{min-height:560px;padding:38px 26px;gap:14px;justify-content:center}
.rrw.mobile .hf{min-height:calc(100% - 118px)}
.rrw.mobile .hero h1{font-size:33px;line-height:1.08}
.rrw.mobile .hero .lead{font-size:14px}
.rrw.mobile .hero .row{justify-content:flex-start}
.rrw.mobile .pagehero{padding:40px 24px}.rrw.mobile .pagehero h1{font-size:30px}
.rrw.mobile .sec{padding:34px 22px}
.rrw.mobile .sec-h{font-size:24px}
.rrw.mobile .lead{font-size:14px}
.rrw.mobile .eyebrow{font-size:10px}
.rrw.mobile .strip{flex-wrap:nowrap;overflow-x:auto;-webkit-overflow-scrolling:touch;padding:0 22px 34px;gap:10px}
.rrw.mobile .strip .ph{flex:0 0 70%;height:180px}
.rrw.mobile .catbar{padding:13px 18px;flex-wrap:nowrap;overflow-x:auto;justify-content:flex-start}
.rrw.mobile .catbar.cs-underline{gap:20px}
.rrw.mobile .marq>div{width:120px;height:76px}

/* generic collapse: all inline-grids → one column on mobile */
.rrw.mobile [style*="display:grid"]{grid-template-columns:1fr!important;gap:16px!important}
.rrw.mobile .grid3,.rrw.mobile .dishgrid{grid-template-columns:1fr!important}
.rrw.mobile .form{grid-template-columns:1fr!important}

/* footer: 4-col → 2-col, rest stacks */
.rrw.mobile .ftr .cols{grid-template-columns:1fr 1fr!important;gap:22px 18px!important}
.rrw.mobile .ftr div[style*="display:flex"]{flex-wrap:wrap}
.rrw.mobile .ftr .ph{flex:1 1 44%!important;min-width:120px}
.rrw.mobile .ftr{padding:28px 20px 32px!important}
.rrw.mobile .ftr [style*="grid-template-columns"]{grid-template-columns:1fr!important;gap:16px!important}
.rrw.mobile .ftr [style*="repeat(3"]{grid-template-columns:1fr 1fr!important}
.rrw.mobile .ftr>h4{font-size:22px!important;line-height:1.15!important;margin-bottom:14px!important}
.rrw.mobile .ftr h4{font-size:15px!important}
.rrw.mobile .ftr a,.rrw.mobile .ftr span,.rrw.mobile .ftr p{font-size:12.5px!important}
.rrw.mobile .ftr .ph.img{height:118px!important}
.rrw.mobile .split{flex-direction:column;gap:22px}
.rrw.mobile .split .ph{width:100%;min-height:200px;flex:none}
.rrw.mobile .menu-side{flex-direction:column}
.rrw.mobile .menu-body{padding-top:8px}

/* ── Hero ── */
.rrw .hero{min-height:clamp(320px,46vh,460px);display:flex;flex-direction:column;justify-content:center;padding:clamp(44px,6vw,80px) clamp(28px,5vw,56px);gap:16px}
/* image heroes: anchor text/CTA to lower-left */
.rrw .hero.ph.img:not([style*="text-align:center"]){justify-content:flex-end;padding-bottom:clamp(40px,5vw,60px)}
.rrw.mobile .hero.ph.img:not([style*="text-align:center"]){padding-bottom:48px}
.rrw .hero h1{font-size:clamp(40px,5.6vw,72px);color:var(--c-head);text-wrap:balance}
.rrw .row{display:flex;gap:12px;flex-wrap:wrap}
.rrw .hero .row{margin-top:8px}
.rrw .pagehero{padding:clamp(48px,6vw,84px) clamp(28px,5vw,56px);background:var(--c-bg-muted);text-align:center}
.rrw .pagehero h1{font-size:clamp(34px,4.5vw,56px);color:var(--c-head)}
.rrw .pagehero p{color:var(--c-text);opacity:.65;margin-top:8px}

/* ── Cards / sections ── */
.rrw .card{background:var(--c-bg);border:1px solid var(--c-border);border-radius:var(--c-radius);padding:20px}
.rrw .feat{text-align:center}.rrw .feat .ic{font-size:30px;margin-bottom:10px}.rrw .feat h4{font-size:18px;color:var(--c-text);margin-bottom:6px}.rrw .feat p{font-size:13px;color:var(--c-text);opacity:.65}
.rrw .split{display:flex;gap:36px;align-items:center}
.rrw .split .ph{flex:1;min-height:260px;border-radius:var(--c-radius)}.rrw .split .txt{flex:1}
.rrw .strip{display:flex;gap:10px;padding:0 clamp(28px,5vw,56px) clamp(40px,5vw,64px)}
.rrw .strip .ph{flex:1;height:130px;border-radius:var(--c-radius)}
.rrw .quote{font-style:italic;font-size:15px;color:var(--c-text);line-height:1.6;margin-bottom:12px}
.rrw .stars{color:var(--c-accent);letter-spacing:3px;margin-bottom:8px}

/* ── Gallery hero ── */
.rrw .gallery-hero{display:grid;gap:16px}

/* ── Larger marquee bands (marquee-stacked only) ── */
.rrw .marq.lg{gap:6px}
.rrw .marq.lg>div{width:440px;height:300px;border-radius:8px}
.rrw.mobile .marq.lg>div{width:300px;height:200px}

/* ── Slideshow hero ── */
.rrw .hero-slideshow{position:relative;min-height:clamp(380px,52vh,580px);overflow:hidden;display:flex;align-items:flex-end}
.rrw .hero-slideshow .slide{position:absolute;inset:0;opacity:0;transition:opacity .8s ease;background-size:cover;background-position:center}
.rrw .hero-slideshow .slide.active{opacity:1}
.rrw .hero-slideshow .slide-overlay{position:absolute;inset:0;background:linear-gradient(0deg,rgba(0,0,0,.62),rgba(0,0,0,.22) 55%,rgba(0,0,0,.08))}
.rrw .hero-slideshow .slide-content{position:relative;z-index:2;padding:clamp(32px,5vw,56px);color:#fff;width:100%}
.rrw .hero-slideshow .slide-dots{position:absolute;bottom:18px;left:50%;transform:translateX(-50%);z-index:3;display:flex;gap:8px}
.rrw .hero-slideshow .slide-dot{width:8px;height:8px;border-radius:50%;background:rgba(255,255,255,.4);border:none;cursor:pointer;padding:0;transition:background .2s}
.rrw .hero-slideshow .slide-dot.active{background:#fff}
.rrw .hero-slideshow .slide-label{position:absolute;top:18px;right:18px;z-index:3;background:rgba(255,255,255,.18);color:#fff;font-size:11px;font-weight:700;letter-spacing:.12em;padding:5px 12px;border-radius:100px;font-family:var(--c-body)}

/* ── Logo-scroll hero ── */
.rrw .hero-logoscroll{position:relative;min-height:clamp(400px,56vh,620px);overflow:hidden;display:flex;align-items:center;justify-content:center;text-align:center}
.rrw .hero-logoscroll .logoscroll-content{position:relative;z-index:2;padding:clamp(40px,6vw,80px) clamp(28px,5vw,56px);max-width:860px;width:100%;will-change:transform,opacity}

/* ── Video-loop hero ── */
.rrw .hero-videoloop{position:relative;min-height:clamp(380px,52vh,580px);overflow:hidden;display:flex;align-items:center;justify-content:center;text-align:center}
.rrw .hero-videoloop video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.rrw .hero-videoloop .videoloop-overlay{position:absolute;inset:0;background:linear-gradient(180deg,rgba(0,0,0,.28),rgba(0,0,0,.48))}
.rrw .hero-videoloop .videoloop-content{position:relative;z-index:2;padding:clamp(40px,6vw,80px) clamp(28px,5vw,56px);max-width:780px;width:100%;color:#fff}
.rrw .hero-videoloop .play-affordance{width:72px;height:72px;border-radius:50%;background:rgba(255,255,255,.18);display:flex;align-items:center;justify-content:center;font-size:26px;color:#fff;margin:0 auto 20px}

/* ── Split-info hero ── */
.rrw .hero-splitinfo{display:flex;min-height:clamp(400px,54vh,560px)}
.rrw .hero-splitinfo .splitinfo-panel{flex:1;display:flex;flex-direction:column;justify-content:center;padding:clamp(36px,5vw,64px) clamp(28px,4vw,52px);gap:14px}
.rrw .hero-splitinfo .hours-card{border:1px solid var(--c-border);border-radius:var(--c-radius);padding:14px 18px;background:var(--c-bg-muted);font-size:13px;display:flex;flex-direction:column;gap:6px;margin-top:4px}
.rrw .hero-splitinfo .hours-row{display:flex;justify-content:space-between;color:var(--c-text);opacity:.8}
.rrw.mobile .hero-splitinfo{flex-direction:column}
.rrw.mobile .hero-splitinfo>.ph{min-height:220px!important;flex:none!important}

/* ── Lightbox (opt-in fullscreen image viewer; portaled to body, root carries .rrw) ── */
.rrw.rrw-lb{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.92);touch-action:none;animation:rrw-lb-fade .18s ease}
.rrw .rrw-lb__stage{position:relative;z-index:1;width:100%;height:100%;display:flex;align-items:center;justify-content:center;padding:clamp(1rem,4vw,3rem);cursor:zoom-out}
.rrw .rrw-lb__img{cursor:default}
.rrw .rrw-lb__img{max-width:100%;max-height:100%;object-fit:contain;user-select:none;-webkit-user-drag:none;box-shadow:0 10px 40px rgba(0,0,0,.5);animation:rrw-lb-zoom .2s ease}
.rrw .rrw-lb__btn{position:absolute;z-index:3;background:rgba(0,0,0,.4);color:#fff;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:999px;transition:background .15s ease,transform .15s ease;-webkit-tap-highlight-color:transparent}
.rrw .rrw-lb__btn:hover{background:rgba(255,255,255,.18)}
.rrw .rrw-lb__btn:active{transform:scale(.92)}
.rrw .rrw-lb__btn svg{width:24px;height:24px;stroke:currentColor;stroke-width:2;fill:none}
.rrw .rrw-lb__close{top:clamp(.75rem,2vw,1.5rem);right:clamp(.75rem,2vw,1.5rem);width:44px;height:44px}
.rrw .rrw-lb__nav{top:50%;transform:translateY(-50%);width:48px;height:48px}
.rrw .rrw-lb__nav:active{transform:translateY(-50%) scale(.92)}
.rrw .rrw-lb__prev{left:clamp(.5rem,2vw,1.5rem)}
.rrw .rrw-lb__next{right:clamp(.5rem,2vw,1.5rem)}
.rrw .rrw-lb__count{position:absolute;bottom:clamp(.75rem,2vw,1.5rem);left:50%;transform:translateX(-50%);color:rgba(255,255,255,.85);font-size:.85rem;letter-spacing:.05em;font-variant-numeric:tabular-nums}
@keyframes rrw-lb-fade{from{opacity:0}to{opacity:1}}
@keyframes rrw-lb-zoom{from{opacity:0;transform:scale(.97)}to{opacity:1;transform:scale(1)}}
@media (prefers-reduced-motion:reduce){.rrw.rrw-lb,.rrw .rrw-lb__img{animation:none}}

/* ── Menu block ── */
/* category heading */
.rrw .menu .cat{font-family:var(--c-heading);font-size:22px;color:var(--c-text);margin:22px 0 12px;border-bottom:2px solid var(--c-border);padding-bottom:7px}
/* item row */
.rrw .menu .it{display:flex;justify-content:space-between;gap:12px;margin-bottom:8px}
.rrw .menu .it .nm{font-weight:600;color:var(--c-text)}
.rrw .menu .it .pr{color:var(--c-accent);font-weight:600;white-space:nowrap}
.rrw .menu .it .ds,.rrw .menu .ds{font-size:12.5px;color:var(--c-text);opacity:.6;margin:-4px 0 10px}
.rrw .menu .it .leader{flex:1;border-bottom:1.5px dotted var(--c-border);margin:0 8px;position:relative;top:-5px}
/* accordion */
.rrw .acc{border-bottom:1px solid var(--c-border)}
.rrw .acc-h{display:flex;justify-content:space-between;align-items:center;padding:16px 2px;font-family:var(--c-heading);font-size:21px;color:var(--c-text);cursor:pointer}
.rrw .acc-x{font-size:22px;color:var(--c-accent);font-weight:400}
.rrw .acc-b{padding:0 2px 14px}
/* tabbed-categories panels */
.rrw .mpanel{display:none}
.rrw .mpanel.on{display:block}
.rrw .mv-tabbed .menu-body{padding-top:20px}
.rrw .mv-tabbed .catbar .ct{cursor:pointer}
/* category nav bar */
.rrw .catbar{position:sticky;top:0;z-index:2;display:flex;gap:8px;flex-wrap:wrap;justify-content:center;padding:16px clamp(28px,5vw,56px);background:var(--c-bg);border-bottom:1px solid var(--c-border)}
.rrw .catbar.sticky{box-shadow:0 1px 0 var(--c-border)}
.rrw .catbar .ct{padding:8px 17px;border:1px solid var(--c-border);font-size:12.5px;font-weight:600;color:var(--c-text);letter-spacing:.02em;cursor:pointer;transition:.14s;white-space:nowrap}
/* pills style */
.rrw .catbar.cs-pills .ct{border-radius:100px}
.rrw .catbar.cs-pills .ct.on{background:var(--c-accent);color:var(--c-on-accent);border-color:var(--c-accent)}
/* mobile catbar */
.rrw.mobile .catbar{padding:13px 18px;flex-wrap:nowrap;overflow-x:auto;justify-content:flex-start}

/* ── Menu block — Task 2: list/image variants + scroll-to-category nav ── */

/* internal scroll container for stacked list/image variants */
.rrw .menu-scroll{max-height:560px;overflow-y:auto}

/* dotted-list: classic centered serif headings with side rules, dotted leader */
.rrw .mv-dotted .cat-classic{font-family:var(--c-heading);font-size:24px;text-align:center;border-bottom:none;display:flex;align-items:center;gap:16px;margin:30px 0 16px;color:var(--c-text)}
.rrw .mv-dotted .cat-classic::before,.rrw .mv-dotted .cat-classic::after{content:'';flex:1;height:1px;background:var(--c-border)}
.rrw .mv-dotted .it{margin-bottom:6px}
.rrw .mv-dotted .it .leader{flex:1;border-bottom:2px dotted color-mix(in oklab,var(--c-border) 90%,var(--c-text));margin:0 6px;position:relative;top:-5px}
.rrw .mv-dotted .ds{text-align:left}

/* simple-list: airy, left-aligned, small uppercase category labels, accent price */
.rrw .mv-simple .cat{font-family:var(--c-body);font-size:12px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--c-accent);border-bottom:none;margin:30px 0 14px;padding:0;opacity:.9}
.rrw .mv-simple .it{margin-bottom:14px;align-items:baseline}
.rrw .mv-simple .it .nm{font-weight:500;font-size:16px}
.rrw .mv-simple .it .pr{font-size:15px}

/* two-column-list: items in a 2-col grid per category */
.rrw .mv-twocol .twocol-grid{display:grid;grid-template-columns:1fr 1fr;gap:0 34px}
.rrw.mobile .mv-twocol .twocol-grid{grid-template-columns:1fr}

/* dish-grid cards (category-hero-list) */
.rrw .dishgrid{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:4px}
.rrw .dishcard{border:1px solid var(--c-border);border-radius:var(--c-radius);overflow:hidden;background:var(--c-bg-muted)}
.rrw .dishcard .ph{height:130px}
.rrw .dishcard .dc-b{padding:12px 14px}
.rrw .dishcard .ds{margin:2px 0 0}
.rrw.mobile .dishgrid{grid-template-columns:1fr}

/* category-banner-list: neutral .ph banner strip per category */
.rrw .cat-banner{height:120px;border-radius:var(--c-radius);position:relative;overflow:hidden;margin:26px 0 14px;display:flex;align-items:center;justify-content:center}
.rrw .mcat:first-child .cat-banner{margin-top:14px}
.rrw .cat-banner-scrim{position:absolute;inset:0;background:rgba(0,0,0,.4)}

/* ── Footer ── */
.rrw .ftr{background:var(--c-text);color:var(--c-bg);padding:clamp(36px,4vw,52px) clamp(28px,5vw,56px)}
.rrw .ftr a,.rrw .ftr span,.rrw .ftr p{color:var(--c-bg);opacity:.85;font-size:13px}
.rrw .ftr .cols{display:grid;gap:26px}
.rrw .ftr h4{font-family:var(--c-heading);font-size:16px;margin-bottom:10px;opacity:1;color:var(--c-bg)}
/* newsletter footer panel */
.rrw .nl-panel{display:grid;grid-template-columns:1.1fr 1fr;gap:clamp(24px,4vw,56px);align-items:center;background:linear-gradient(135deg,color-mix(in oklab,var(--c-accent) 92%,#000),var(--c-accent));border-radius:calc(var(--c-radius) + 6px);padding:clamp(28px,3.4vw,44px)}
.rrw .nl-eyebrow{font-family:var(--c-body);font-size:11px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:var(--c-on-accent);opacity:.7;margin-bottom:10px}
.rrw .nl-h{font-family:var(--c-heading);font-size:clamp(22px,2.5vw,32px);line-height:1.12;color:var(--c-on-accent);margin:0 0 8px;opacity:1}
.rrw .nl-sub{color:var(--c-on-accent)!important;opacity:.82!important;font-size:14px!important;margin:0;max-width:340px}
.rrw .nl-field{display:flex;align-items:center;gap:8px;background:var(--c-bg);border-radius:100px;padding:6px 6px 6px 16px;box-shadow:0 10px 30px -12px rgba(0,0,0,.5)}
.rrw .nl-mail{color:var(--c-text);opacity:.4;font-size:15px}
.rrw .nl-ph{flex:1;color:var(--c-text);opacity:.5;font-size:14px;white-space:nowrap;overflow:hidden}
.rrw .nl-btn{background:var(--c-accent);color:var(--c-on-accent);font-weight:700;font-size:13px;padding:11px 22px;border-radius:100px;white-space:nowrap;cursor:pointer}
.rrw .nl-fine{color:var(--c-on-accent)!important;opacity:.62!important;font-size:11.5px!important;margin-top:10px}
.rrw .nl-links{display:flex;flex-wrap:wrap;gap:8px 22px;margin-top:22px}
.rrw .nl-links a{font-weight:600;opacity:.8}
.rrw.mobile .nl-panel{grid-template-columns:1fr;gap:20px;text-align:left}
.rrw.mobile .nl-h{font-size:22px!important}
.rrw .cat-banner-label{position:relative;z-index:1;color:#fff;font-family:var(--c-heading);font-size:26px;letter-spacing:.02em}

/* ── Menu category-nav buttons (.ct) follow the chosen button-style token (btn-*) ── */
.rrw.btn-solid-rounded .catbar .ct,.rrw.btn-outline-rounded .catbar .ct{border-radius:100px}
.rrw.btn-solid-square .catbar .ct,.rrw.btn-outline-square .catbar .ct{border-radius:0}
.rrw.btn-solid-soft .catbar .ct{border-radius:8px}
.rrw.btn-ghost-underline .catbar .ct{border:none;border-radius:0;padding:8px 4px;border-bottom:2px solid transparent}
.rrw.btn-ghost-underline .catbar .ct.on{background:transparent;color:var(--c-accent);border-bottom-color:var(--c-accent)}

/* ── USP: four-stat-band ── */
.rrw .usp-band{display:flex;gap:18px;flex-wrap:wrap;justify-content:center;text-align:center}
.rrw .usp-band-item{flex:1;min-width:160px;padding:22px 16px;background:var(--c-bg-muted);border-radius:var(--c-radius)}
.rrw .usp-band-title{font-family:var(--c-heading);font-size:19px;color:var(--c-head);margin-bottom:6px}
.rrw .usp-band-body{font-size:13.5px;color:var(--c-text);opacity:.72;line-height:1.55}
.rrw.mobile .usp-band-item{min-width:calc(50% - 9px)}

/* ── Reviews: logo-quote-strip ── */
.rrw .review-strip{display:flex;gap:32px;justify-content:center;flex-wrap:wrap}
.rrw .review-strip-item{flex:1;min-width:200px;max-width:300px;text-align:center;padding:0 8px;border-left:1px solid var(--c-border)}
.rrw .review-strip-item:first-child{border-left:none}
.rrw .review-strip-item .quote{font-size:14px}
.rrw.mobile .review-strip-item{min-width:100%;border-left:none;border-top:1px solid var(--c-border);padding-top:18px}
.rrw.mobile .review-strip-item:first-child{border-top:none;padding-top:0}

/* ── Contact: map placeholder ── */
.rrw .contact-map{flex:1;min-height:280px;border-radius:var(--c-radius)}

/* ── Events: date-cards + list-rows ── */
.rrw .event-date{display:inline-flex;flex-direction:column;align-items:center;justify-content:center;width:56px;padding:8px 0;background:var(--c-bg-muted);border-radius:var(--c-radius);line-height:1;margin-bottom:12px}
.rrw .event-day{font-family:var(--c-heading);font-size:24px;color:var(--c-head)}
.rrw .event-month{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--c-accent);font-weight:700;margin-top:3px}
.rrw .event-title{font-size:18px;color:var(--c-head);margin-bottom:6px}
.rrw .event-body{font-size:14px;color:var(--c-text);opacity:.72;line-height:1.6}
.rrw .event-row{display:flex;gap:22px;align-items:flex-start;padding:18px 0;border-top:1px solid var(--c-border)}
.rrw .event-row:first-of-type{border-top:none}
.rrw .event-date-inline{margin-bottom:0;background:var(--c-bg);border:1px solid var(--c-border)}

/* ── Lunch: weekly-list + card-grid ── */
.rrw .lunch-day{display:flex;gap:18px;align-items:baseline;padding:12px 0;border-bottom:1px solid var(--c-border)}
.rrw .lunch-day .d{width:110px;flex-shrink:0;font-family:var(--c-heading);font-size:16px;color:var(--c-head)}
.rrw .lunch-day .m{color:var(--c-text);opacity:.82;font-size:15px}
.rrw .lunch-card-day{font-family:var(--c-heading);font-size:15px;color:var(--c-accent);margin-bottom:6px}
.rrw .lunch-card-dish{font-size:14px;color:var(--c-text);opacity:.82;line-height:1.5}
.rrw.mobile .lunch-day .d{width:88px;font-size:14px}

/* ── Groups: banner-overlay ── */
.rrw .groups-banner{min-height:320px;border-radius:var(--c-radius);position:relative;overflow:hidden;display:flex;align-items:center;justify-content:center}
.rrw .groups-banner-scrim{position:absolute;inset:0;background:linear-gradient(0deg,rgba(0,0,0,.55),rgba(0,0,0,.35))}
.rrw .groups-banner-inner{position:relative;text-align:center;max-width:600px;padding:32px 24px}

/* ── GroupMenu: price-rows + cards ── */
.rrw .gm-row{display:flex;justify-content:space-between;align-items:baseline;gap:20px;padding:18px 0;border-bottom:1px solid var(--c-border)}
.rrw .gm-name{font-family:var(--c-heading);font-size:19px;color:var(--c-head)}
.rrw .gm-desc{opacity:.62;font-size:13px;color:var(--c-text)}
.rrw .gm-price{white-space:nowrap;font-weight:700;color:var(--c-accent)}

/* ── Embed: placeholder slot ── */
.rrw .embed-ph{max-width:640px;margin:0 auto;padding:26px 20px;border:1.5px dashed var(--c-border);border-radius:14px;text-align:center}

/* ── Portion: size picker for anything sold by weight ── */
.rrw .pv-hero{display:flex;gap:clamp(24px,4vw,56px);align-items:center}
.rrw .pv-copy{flex:1 1 0;min-width:0}
.rrw .pv-amount{font-family:var(--c-heading);font-size:clamp(38px,5vw,64px);line-height:1;color:var(--c-head);margin:0 0 4px}
.rrw .pv-amount sub{font-family:var(--c-body);font-size:13px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;opacity:.6;vertical-align:baseline;margin-left:8px}

/* every line in one cell, only the chosen one visible: the cell is then as tall
   as the longest of them and the block cannot jump while dragging */
.rrw .pv-lines{display:grid;margin:0 0 20px;max-width:34rem}
.rrw .pv-line{grid-area:1/1;visibility:hidden;opacity:0;transition:opacity .18s ease}
.rrw .pv-line[data-active="true"]{visibility:visible;opacity:1}
.rrw .pv-line-row{display:block;font-size:15px;color:var(--c-text)}
.rrw .pv-line-reply{opacity:.62;margin-top:2px}

.rrw .pv-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.rrw .pv-hint{display:block;font-size:12px;letter-spacing:.06em;color:var(--c-accent);margin-bottom:6px}

/* own track: accent-color paints the fill and thumb but leaves the UA groove,
   which reads as a progress meter rather than a set of discrete portions */
.rrw .pv-range{-webkit-appearance:none;appearance:none;width:100%;max-width:26rem;margin:0 0 18px;background:transparent;--pv-thumb:22px;--pv-thumb-ar:1;--pv-thumb-h:calc(var(--pv-thumb)/var(--pv-thumb-ar))}
.rrw .pv-range::-webkit-slider-runnable-track{height:4px;border-radius:2px;background:color-mix(in oklab,var(--c-text) 22%,transparent)}
.rrw .pv-range::-moz-range-track{height:4px;border-radius:2px;background:color-mix(in oklab,var(--c-text) 22%,transparent)}

/* the thumb grows with the portion; width, height and offset all derive from
   one number so the three animate together. Webkit and Moz cannot be combined
   into one rule: a selector either browser rejects drops the whole block. */
.rrw .pv-range::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:var(--pv-thumb);height:var(--pv-thumb-h);margin-top:calc((4px - var(--pv-thumb-h))/2);border:0;border-radius:var(--pv-thumb-r,50%);background:var(--pv-thumb-img,none) no-repeat center/contain,var(--c-accent);cursor:pointer;transition:width .2s linear,height .2s linear,margin-top .2s linear}
.rrw .pv-range::-moz-range-thumb{width:var(--pv-thumb);height:var(--pv-thumb-h);border:0;border-radius:var(--pv-thumb-r,50%);background:var(--pv-thumb-img,none) no-repeat center/contain,var(--c-accent);cursor:pointer;transition:width .2s linear,height .2s linear}
.rrw .pv-range[style*="--pv-thumb-img"]::-webkit-slider-thumb{border-radius:0;background-color:transparent}
.rrw .pv-range[style*="--pv-thumb-img"]::-moz-range-thumb{border-radius:0;background-color:transparent}

.rrw .pv-chips{display:grid;grid-template-columns:repeat(auto-fit,minmax(0,1fr));grid-auto-rows:1fr;gap:8px;max-width:26rem;list-style:none;padding:0;margin:0}
.rrw .pv-chip{width:100%;height:100%;padding:10px 8px;font:inherit;font-size:12px;font-weight:600;line-height:1.25;color:var(--c-text);background:transparent;border:1.5px solid var(--c-border);border-radius:var(--c-radius);cursor:pointer;transition:border-color .16s,color .16s,background .16s}
.rrw .pv-chip[aria-pressed="true"]{border-color:var(--c-accent);color:var(--c-accent);background:color-mix(in oklab,var(--c-accent) 8%,transparent)}

/* the outgoing picture stays opaque one layer down while the new one fades in
   on top, so the plate never dips dark mid-drag */
.rrw .pv-plate{flex:0 0 clamp(240px,32vw,26rem);position:relative;aspect-ratio:1;border-radius:var(--c-radius);overflow:hidden}
.rrw .pv-shot{position:absolute;inset:0;opacity:0;transition:opacity .28s ease}
.rrw .pv-shot[data-leaving="true"]{opacity:1;transition:none}
.rrw .pv-shot[data-active="true"]{opacity:1;z-index:1}

.rrw.mobile .pv-hero{flex-direction:column;align-items:stretch}
.rrw.mobile .pv-plate{flex:none;width:100%}
.rrw.mobile .pv-lines{max-width:none}
.rrw.mobile .pv-chips{grid-template-columns:repeat(2,minmax(0,1fr))}

@media (prefers-reduced-motion:reduce){
  .rrw .pv-range::-webkit-slider-thumb{transition:none}
  .rrw .pv-range::-moz-range-thumb{transition:none}
  .rrw .pv-shot,.rrw .pv-line{transition:none}}

/* ── Maker: credits the hand behind the room ── */
.rrw{--c-hand:'Segoe Script','Bradley Hand','Brush Script MT',cursive}
.rrw .mk-grid{display:flex;gap:clamp(28px,5vw,72px);align-items:center}
.rrw .mk-copy{flex:1 1 0;min-width:0;max-width:34rem}

/* the scene is a proportion, so every card position below is a share of it and
   nothing has to be recomputed when the block moves */
/* The cards lean out of the scene, so the scene needs air of its own to lean
   into — otherwise they reach across the gap and sit on the copy. The source
   solved this with a dedicated grid column plus overflow-x:clip on its wrapper;
   here the same job is done by keeping the outset inside the scene and clipping
   what still escapes. */
.rrw .mk-scene{flex:0 0 clamp(260px,38vw,30rem);position:relative;aspect-ratio:44/var(--mk-scene-h,70);
  overflow:clip;overflow-clip-margin:24px;
  --mk-card-w:31;--mk-card-top:0;--mk-card-step:17;--mk-card-out:1;
  --mk-lean-l:3;--mk-lean-r:3;--mk-card-offset:4}

/* one formula for every card: distance from its own edge, negative = outside
   the scene. A seventh card inherits its place instead of needing a new pair. */
.rrw .mk-card{position:absolute;margin:0;width:calc(var(--mk-card-w) * 1%);
  top:calc((var(--mk-card-top) + var(--shift,0) + var(--row) * var(--mk-card-step)) * 1%);
  rotate:var(--lean);z-index:1;
  --edge:calc(var(--mk-card-out) - var(--row) * var(--lean-col))}
.rrw .mk-card:nth-child(odd){--lean-col:var(--mk-lean-l);left:calc(var(--edge) * 1%)}
.rrw .mk-card:nth-child(even){--lean-col:var(--mk-lean-r);right:calc(var(--edge) * 1%);--shift:var(--mk-card-offset)}
.rrw .mk-card-img{width:100%;aspect-ratio:1;border-radius:calc(var(--c-radius) / 2);box-shadow:0 12px 28px -10px rgba(0,0,0,.5)}

.rrw .mk-figure{position:absolute;inset:auto 0 0;margin:0;display:flex;flex-direction:column;align-items:center;z-index:3}
/* A soft mask on the picture's own edges, so a plain rectangular photo joins
   the scene instead of sitting on it as a box. A real alpha cut-out is already
   transparent there and loses nothing, so this is free in both directions —
   which matters, because most portraits arrive as rectangles. */
.rrw .mk-portrait{width:min(62%,17rem);aspect-ratio:3/4;background-size:contain;background-position:bottom center;background-repeat:no-repeat;
  -webkit-mask:linear-gradient(180deg,transparent,#000 12% 82%,transparent),linear-gradient(90deg,transparent,#000 8% 92%,transparent);
  -webkit-mask-composite:source-in;
  mask:linear-gradient(180deg,transparent,#000 12% 82%,transparent),linear-gradient(90deg,transparent,#000 8% 92%,transparent);
  mask-composite:intersect}
.rrw .mk-portrait:not([style*="background-image"]){background:linear-gradient(180deg,transparent,color-mix(in oklab,var(--c-text) 28%,transparent));border-radius:999px 999px 0 0}
.rrw .mk-name{font-size:clamp(20px,2.4vw,30px);color:var(--c-accent);margin-top:-6px}
.rrw .mk-name-hand{font-family:var(--c-hand);font-size:clamp(26px,3.2vw,40px);line-height:1}

/* stencils over a fill: the alpha does the tonal work, so one file works on any
   ground and in any colour. In front of the figure, so the overlap is ink. */
.rrw .mk-tools{position:absolute;left:50%;bottom:var(--mk-tools-floor,0%);width:var(--mk-tools-w,86%);
  transform:translateX(-50%) rotate(-3.2deg);z-index:4;aspect-ratio:2/1;display:block}
.rrw .mk-tool{position:absolute;inset:0;background:var(--c-accent);
  -webkit-mask:var(--mk-src) no-repeat center/contain;mask:var(--mk-src) no-repeat center/contain}
.rrw .mk-tool[data-layer="1"]{background:color-mix(in oklab,var(--c-accent) 70%,#fff)}

/* the entrance: he stands first, the work pops out from behind him, the tools
   fall last. Order matters — it says the room came out of him. */
.rrw .mk[data-seen="false"] .mk-card{opacity:0;translate:0 calc(40% - var(--row) * 12%);scale:.92}
.rrw .mk[data-seen="false"] .mk-figure{opacity:0;translate:0 6%}
.rrw .mk[data-seen="false"] .mk-tools{opacity:0;translate:0 -18%;rotate:8deg}
.rrw .mk-card,.rrw .mk-figure,.rrw .mk-tools{transition:opacity .62s ease,translate .62s cubic-bezier(.2,.8,.3,1),scale .62s ease,rotate .62s ease}
.rrw .mk[data-seen="true"] .mk-figure{transition-delay:120ms}
.rrw .mk[data-seen="true"] .mk-card{transition-delay:calc(460ms + var(--i) * 80ms)}
.rrw .mk[data-seen="true"] .mk-tools{transition-delay:1180ms}

/* variants */
.rrw .mk-figure-bottom .mk-grid{align-items:flex-end}
.rrw .mk-figure-bottom .mk-scene{--mk-scene-h:60}
.rrw .mk-figure-bottom .mk-tools{opacity:.32}
.rrw .mk-light-panel .mk-scene{isolation:isolate}
.rrw .mk-light-panel .mk-scene::before{content:'';position:absolute;inset:8% 4% 0;border-radius:999px 999px 0 0;
  background:radial-gradient(60% 50% at 50% 70%,color-mix(in oklab,var(--c-accent) 26%,transparent),transparent 70%)}
.rrw .mk-light-panel .mk-tools{opacity:.24}
.rrw .mk-exhibition .mk-scene{--mk-card-w:30;--mk-card-step:19}
.rrw .mk-exhibition .mk-portrait{width:min(48%,13rem)}
.rrw .mk-exhibition .mk-name{position:relative;padding-right:4.5rem}
.rrw .mk-exhibition .mk-name::after{content:'';position:absolute;right:0;top:50%;width:3.5rem;height:1px;background:var(--c-accent)}
.rrw .mk-exhibition .mk-tools{opacity:.2;--mk-tools-w:52%}
.rrw .mk-cards-only .mk-scene{--mk-card-w:36;--mk-card-top:3;--mk-card-step:23;--mk-card-out:3;--mk-lean-l:1;--mk-lean-r:1;--mk-card-offset:6}
.rrw .mk-cards-only .mk-tools{--mk-tools-w:46%;opacity:.28}

.rrw.mobile .mk-grid{flex-direction:column;align-items:stretch}
.rrw.mobile .mk-scene{flex:none;width:100%;aspect-ratio:40/var(--mk-scene-h-m,62);
  --mk-card-w:30;--mk-card-top:-1;--mk-card-step:14;--mk-card-out:1;--mk-lean-l:2;--mk-lean-r:2;--mk-card-offset:3}
.rrw.mobile .mk-copy{max-width:none}

@media (prefers-reduced-motion:reduce){
  .rrw .mk-card,.rrw .mk-figure,.rrw .mk-tools{transition:none}}

/* ── Book: a leafable printed volume, read in the shared lightbox ── */
.rrw .bk{--bk-aspect:.72}
.rrw .bk-poster{display:flex;justify-content:center}
.rrw .bk-cover{display:flex;flex-direction:column;align-items:center;gap:16px;padding:0;background:none;border:0;cursor:pointer;font:inherit}
.rrw .bk-cover-face{display:block;width:min(20rem,62vw);aspect-ratio:var(--bk-aspect);border-radius:2px 8px 8px 2px;
  box-shadow:0 26px 60px -20px rgba(0,0,0,.55),inset 14px 0 22px -18px rgba(0,0,0,.7);transition:transform .3s cubic-bezier(.2,.8,.3,1),box-shadow .3s}
.rrw .bk-cover:hover .bk-cover-face{transform:translateY(-6px) rotate(-1deg);box-shadow:0 34px 70px -20px rgba(0,0,0,.6),inset 14px 0 22px -18px rgba(0,0,0,.7)}
.rrw .bk-cover-label{font-size:13px;font-weight:600;letter-spacing:.06em;color:var(--c-accent)}

/* the gutter is the seam between the two leaves: an inner shadow on each,
   facing the middle, which is what makes a flat pair read as a bound book */
.rrw .bk-spread-wrap{display:flex;flex-direction:column;align-items:center;gap:18px}
.rrw .bk-spread{display:flex;gap:2px;width:min(46rem,100%);filter:drop-shadow(0 24px 50px rgba(0,0,0,.4))}
.rrw .bk-leaf{flex:1 1 0;aspect-ratio:var(--bk-aspect);padding:0;border:0;cursor:pointer;display:block}
.rrw .bk-leaf:first-child{border-radius:6px 0 0 6px;box-shadow:inset -18px 0 26px -20px rgba(0,0,0,.85)}
.rrw .bk-leaf:last-child{border-radius:0 6px 6px 0;box-shadow:inset 18px 0 26px -20px rgba(0,0,0,.85)}
.rrw .bk-leaf-blank{background:var(--c-bg-muted);cursor:default}
.rrw .bk-controls{display:flex;align-items:center;gap:16px}
.rrw .bk-controls .cbtn{padding:8px 18px;font-size:18px;line-height:1}
.rrw .bk-controls .cbtn:disabled{opacity:.35;cursor:default}
.rrw .bk-count{font-size:13px;opacity:.7;min-width:4.5rem;text-align:center}

.rrw .bk-strip{display:flex;gap:10px;overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:12px}
.rrw .bk-strip-page{flex:0 0 auto;width:clamp(120px,14vw,168px);aspect-ratio:var(--bk-aspect);padding:0;border:0;cursor:pointer;
  border-radius:4px;box-shadow:0 10px 24px -12px rgba(0,0,0,.5);transition:transform .2s}
.rrw .bk-strip-page:hover{transform:translateY(-4px)}

/* the spread stacks rather than hiding its right leaf: hiding it would drop
   every second page, because the control still steps a spread at a time */
.rrw.mobile .bk-spread{width:100%;flex-direction:column;gap:10px}
.rrw.mobile .bk-spread .bk-leaf{border-radius:6px;box-shadow:none}
.rrw.mobile .bk-cover-face{width:min(14rem,70%)}
.rrw.mobile .bk-strip-page{width:120px}

@media (prefers-reduced-motion:reduce){
  .rrw .bk-cover-face,.rrw .bk-strip-page{transition:none}}

/* ═══ Portade block, 2026-08-17 ═══ */

/* ── USP: process variants ────────────────────────────────────────────────────
   Ported from restaurang-moldau/tools/bygg_takter.py (A Bandet, B Uppslaget).
   Nothing here touches the three variants that were already in the block.

   A third variant, process-dark, shipped with these and was retired the same
   day; its rules and its --usp-gold tone went with it.

   Two ways to show one sequence of steps:
     .usp-band-*    process-band    frames edge to edge, one continuous arc
     .usp-spread-*  process-spread  magazine rows, alternating, hollow numerals

   COUNT: every layout is laid out from the number of steps. --usp-n carries it
   for the grid that needs an exact column count. Three steps and seven steps
   both hold.

   GOLD: the theme has no gold token, and hard-coding one would break the first
   customer whose accent is not warm. The tone is mixed out of --c-accent
   instead, toward the ink, so it survives a light ground. The "gold" is
   therefore whatever the customer's accent is, at the lightness the ground
   needs. Same for the rules and scrims — they are the theme's own ink and
   accent at low alpha, never a grey or a black picked by hand. */

.rrw .usp-steps{
  --usp-gold-ink:color-mix(in oklab,var(--c-accent) 74%,var(--c-text));
  --usp-rule:color-mix(in oklab,var(--c-accent) 28%,transparent);
}
.rrw .usp-steps ol{list-style:none;margin:0;padding:0}
.rrw .usp-steps li{margin:0}

/* The entrance: once, on load, and staggered by the item's own index so the
   sequence arrives in the order it is meant to be read. Declared only where
   motion is welcome — under reduced motion the rule never exists, which is not
   the same as an animation that runs to a stop. */
@media (prefers-reduced-motion:no-preference){
  @keyframes usp-step-in{from{opacity:0;translate:0 1.1rem}}
  .rrw .usp-band-cell,.rrw .usp-spread-row{
    animation:usp-step-in .8s cubic-bezier(.22,1,.36,1) both;
    animation-delay:calc(120ms + var(--i,0) * 90ms);
  }}

/* ── A: process-band ───────────────────────────────────────────────────────── */
/* Inside the section, not full bleed: the words have to line up with the frames
   above them for the pairing to read, and a full bleed strip over a capped text
   grid puts every sentence out of plumb with its own picture. */
.rrw .usp-band-strip{
  display:grid;grid-template-columns:repeat(var(--usp-n,5),minmax(0,1fr));gap:0;
  margin-top:clamp(20px,3vw,34px);
}
/* Gap 0 belongs to the pictures, not to the prose. Each cell keeps its own
   inline padding under the frame, so the frames touch and the sentences do not. */
.rrw .usp-band-cell{display:grid;grid-template-rows:auto 1fr}
.rrw .usp-band-frame{position:relative}
.rrw .usp-band-shot{aspect-ratio:4/5}

/* The numeral sits ON the photograph, so it needs a contrast floor of its own.
   A scrim confined to the top third costs nothing and never reaches the food,
   which in a process frame is always in the lower two thirds. */
.rrw .usp-band-frame::before{
  content:'';position:absolute;inset:0 0 auto;height:38%;z-index:1;pointer-events:none;
  background:linear-gradient(to bottom,
    color-mix(in oklab,var(--c-text) 70%,transparent) 0%,
    color-mix(in oklab,var(--c-text) 34%,transparent) 55%,
    transparent 100%);
}
.rrw .usp-band-nr{
  position:absolute;z-index:2;
  top:clamp(8px,1.4vw,16px);inset-inline-start:clamp(8px,1.4vw,16px);
  font-family:var(--c-heading);font-size:clamp(17px,2.4vw,30px);line-height:1;
  letter-spacing:.06em;color:var(--c-bg);
}
.rrw .usp-band-word{padding:14px clamp(12px,1.8vw,24px) 0 0}
.rrw .usp-band-cell:last-child .usp-band-word{padding-inline-end:0}
.rrw .usp-band-name{
  margin:0 0 6px;font-size:13px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--usp-gold-ink);
}
.rrw .usp-band-text{margin:0;font-size:13.5px;line-height:1.6;color:var(--c-text);opacity:.76}

/* ── B: process-spread ─────────────────────────────────────────────────────── */
.rrw .usp-spread{margin-top:clamp(18px,3vw,30px)}
.rrw .usp-spread-row{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:clamp(20px,4vw,56px);align-items:center;
  padding:clamp(20px,3.4vw,40px) 0;
}
.rrw .usp-spread-row + .usp-spread-row{border-top:1px solid var(--usp-rule)}

/* Even rows swap sides by explicit column placement, not by row-reverse or
   direction:rtl — both of those also reverse the reading order for a screen
   reader, and the order is the whole content here. Equal columns on both sides:
   with an uneven template and only the placement swapped, the frame inherits
   whichever column it lands in and every other picture comes out a third
   larger, which reads as unrelated pictures rather than one sequence. */
.rrw .usp-spread-row:nth-child(even) .usp-spread-frame{grid-column:2;grid-row:1;justify-self:end}
.rrw .usp-spread-row:nth-child(even) .usp-spread-words{grid-column:1;grid-row:1}

/* Capped, and pushed to the outer edge of its own column: uncapped, a 4:5 frame
   in a half width column turns five sentences into several screens of scroll,
   and justify-self keeps the outer margin straight down the spread instead of
   the frames drifting toward the centre. */
.rrw .usp-spread-frame{width:100%;max-width:24rem;justify-self:start}
.rrw .usp-spread-shot{aspect-ratio:4/5}

.rrw .usp-spread-nr{
  display:block;font-family:var(--c-heading);font-weight:700;
  font-size:clamp(52px,7vw,96px);line-height:.9;
  /* The stroke is painted inside the glyph box, so a tight line-height clips the
     top of the numerals. Give the box the height back without moving anything. */
  padding-top:.08em;margin:-.08em 0 .1em;
  color:transparent;-webkit-text-stroke:1.5px color-mix(in oklab,var(--c-accent) 55%,transparent);
  pointer-events:none;user-select:none;
}
/* Without text-stroke a transparent numeral is no numeral at all. */
@supports not (-webkit-text-stroke:1px currentColor){
  .rrw .usp-spread-nr{color:color-mix(in oklab,var(--c-accent) 28%,transparent);-webkit-text-stroke:0}}
.rrw .usp-spread-name{margin:0 0 8px;font-size:clamp(20px,2.4vw,28px)}
.rrw .usp-spread-text{margin:0;max-width:32rem;font-size:15px;line-height:1.65;color:var(--c-text);opacity:.78}

/* ── Narrow: stack all three ───────────────────────────────────────────────── */
/* Written twice on purpose: the media query is for a real narrow viewport, the
   .rrw.mobile copy is for the library's 390px preview frame, which is a narrow
   container inside a wide window and never triggers a media query. */
@media (max-width:60rem){
  /* Five columns of prose are a column of syllables, not sentences. Stack:
     picture full width, its own words directly under it, and bigger on a phone
     rather than smaller. The arc only reads across a row, so once the row is
     gone the frames are separated by a rule instead of touching. */
  .rrw .usp-band-strip{grid-template-columns:1fr}
  .rrw .usp-band-cell + .usp-band-cell{margin-top:22px;padding-top:22px;border-top:1px solid var(--usp-rule)}
  .rrw .usp-band-word{padding:14px 0 0}
  .rrw .usp-band-name{font-size:14px}
  .rrw .usp-band-text{font-size:15px}
  .rrw .usp-band-nr{font-size:24px}

  .rrw .usp-spread-row,.rrw .usp-spread-row:nth-child(even){grid-template-columns:1fr;gap:16px}
  .rrw .usp-spread-row:nth-child(even) .usp-spread-frame,
  .rrw .usp-spread-row:nth-child(even) .usp-spread-words{grid-column:1;grid-row:auto}
  .rrw .usp-spread-frame,.rrw .usp-spread-row:nth-child(even) .usp-spread-frame{max-width:none;justify-self:stretch}
  .rrw .usp-spread-nr{font-size:48px}}

.rrw.mobile .usp-band-strip{grid-template-columns:1fr}
.rrw.mobile .usp-band-cell + .usp-band-cell{margin-top:22px;padding-top:22px;border-top:1px solid var(--usp-rule)}
.rrw.mobile .usp-band-word{padding:14px 0 0}
.rrw.mobile .usp-band-name{font-size:14px}
.rrw.mobile .usp-band-text{font-size:15px}
.rrw.mobile .usp-band-nr{font-size:24px}

.rrw.mobile .usp-spread-row,.rrw.mobile .usp-spread-row:nth-child(even){grid-template-columns:1fr;gap:16px}
.rrw.mobile .usp-spread-row:nth-child(even) .usp-spread-frame,
.rrw.mobile .usp-spread-row:nth-child(even) .usp-spread-words{grid-column:1;grid-row:auto}
.rrw.mobile .usp-spread-frame,.rrw.mobile .usp-spread-row:nth-child(even) .usp-spread-frame{max-width:none;justify-self:stretch}
.rrw.mobile .usp-spread-nr{font-size:48px}

/* ── Carousel ─────────────────────────────────────────────────────────────────
   Append to packages/core/src/blocks/shared.css. Nothing here is written in
   this file's own colours: every value that carries colour is a --c-* token, so
   the block takes the theme it is dropped into.

   The block is its own container (container-type: inline-size), so every width
   decision below is made against the block and not against the viewport. That
   is what makes the mobile preview — a 390px .rrw.mobile frame inside a wide
   window — behave like a phone instead of like a desktop.

   Ported from a built site. The hand-fitted numbers there (a 2600px wheel, a
   455.6% meter travel) were only correct for that site's card width and its own
   six cards; here they are formulas, and 3 or 12 cards lay out on the same
   rules. See Carousel.tsx for the radius derivation.
   ────────────────────────────────────────────────────────────────────────── */

.rrw .crsl{container-type:inline-size;--crsl-gap:clamp(14px,2.2vw,30px)}
.rrw .crsl-head{margin-bottom:clamp(20px,3vw,34px)}
.rrw .crsl-head .lead{margin:6px 0 0}

/* card skin — identical in all three variants, so a comparison between them is
   a comparison of the mechanic and not of three card designs */
.rrw .crsl-card{margin:0;position:relative}
.rrw .crsl-card-frame{display:block;position:relative;overflow:hidden;aspect-ratio:4/5;border-radius:var(--c-radius)}
.rrw .crsl-card-text{display:grid;gap:2px;padding-top:12px}
.rrw .crsl-card-name{font-family:var(--c-heading);font-weight:600;font-size:17px;line-height:1.2;letter-spacing:.02em;color:var(--c-head);transition:color .3s ease}
.rrw .crsl-card-line{font-size:14px;line-height:1.5;color:var(--c-text);opacity:.72}
/* the numeral is the only type that sits on a photograph, so it carries its own
   ground rather than trusting the picture underneath it */
.rrw .crsl-card-no{position:absolute;z-index:2;top:10px;left:10px;padding:5px 8px;border-radius:calc(var(--c-radius) / 2);font-family:var(--c-heading);font-weight:600;font-size:14px;line-height:1;letter-spacing:.08em;color:var(--c-bg);background:color-mix(in oklab,var(--c-text) 76%,transparent)}

/* one control pair, real buttons at the 44px floor */
.rrw .crsl-ctrl{display:flex;gap:10px;margin:18px 0 0}
.rrw .crsl-btn{min-width:44px;min-height:44px;display:inline-flex;align-items:center;justify-content:center;font:inherit;font-size:17px;color:var(--c-text);background:transparent;border:1.5px solid var(--c-border);border-radius:var(--c-radius);cursor:pointer;transition:background .16s,color .16s,border-color .16s}
.rrw .crsl-btn:hover:not([disabled]){background:var(--c-accent);border-color:var(--c-accent);color:var(--c-on-accent)}
.rrw .crsl-btn[disabled]{opacity:.35;cursor:default}

/* rail shared by deck-fan and center-swell. position:relative so a card's
   offsetLeft is measured against the scroller itself — that is what lets the
   step and the middle card be read off the DOM instead of recomputed in JS. */
.rrw .crsl-rail{position:relative;display:flex;gap:var(--crsl-gap);list-style:none;margin:0;padding:0;overflow-x:auto;scroll-snap-type:x mandatory;overscroll-behavior-x:contain}
.rrw .crsl-rail:focus-visible{outline:2px solid var(--c-accent);outline-offset:4px}
.rrw .crsl-cell{scroll-snap-align:center}
@container (max-width:34rem){
  .rrw .crsl-swell-rail{--crsl-cw:min(20rem,86%)}}

/* ── center-swell ─────────────────────────────────────────────────────────────
   A real scroll-snap rail: native swipe, keyboard scrollable, nothing pins. */
.rrw .crsl-swell-rail{--crsl-cw:min(30rem,78%);padding:6px 0 10px}
.rrw .crsl-swell-cell{flex:0 0 var(--crsl-cw);transform:scale(.88);transform-origin:center bottom;transition:transform .55s cubic-bezier(.25,.46,.45,.94)}
.rrw .crsl-swell-cell[data-active="true"]{transform:scale(1)}
/* same rule as the wheel: the neighbours' photographs step back, their captions
   do not. Scale carries the hierarchy; opacity only helps it. */
.rrw .crsl-swell-cell .crsl-card-frame{opacity:.72;transition:opacity .55s ease}
.rrw .crsl-swell-cell[data-active="true"] .crsl-card-frame{opacity:1}
/* the first and last card need half a rail of air, or snapping to centre cannot
   reach them and the outer cards are never the middle one */
.rrw .crsl-swell-rail::before,.rrw .crsl-swell-rail::after{content:"";flex:0 0 max(0px,calc((100% - var(--crsl-cw)) / 2 - var(--crsl-gap)))}
.rrw .crsl-foot{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:18px}
.rrw .crsl-foot .crsl-ctrl{margin-top:0}
/* the thumb is sized from how much of the rail is on screen and travels by its
   own width, both handed over by the component — no percentage fitted to a
   particular number of cards */
.rrw .crsl-meter{display:block;flex:1 1 160px;min-width:110px;max-width:340px;height:2px;border-radius:2px;overflow:hidden;background:color-mix(in oklab,var(--c-text) 16%,transparent)}
.rrw .crsl-meter i{display:block;height:100%;width:calc(var(--crsl-tw,.25) * 100%);background:var(--c-accent);transform:translateX(calc(var(--crsl-tp,0) * 1%))}

/* ── reduced motion ───────────────────────────────────────────────────────────
   Not "shorter transitions": nothing springs, nothing pins, nothing turns. The
   deck is open from the start and the wheel is the same flat column the narrow
   container gets, so a visitor who asked for less motion still gets every card. */
@media (prefers-reduced-motion:reduce){
  .rrw .crsl-swell-cell,.rrw .crsl-swell-cell[data-active="true"]{transform:none;transition:none}
  .rrw .crsl-swell-cell .crsl-card-frame{opacity:1;transition:none}
  .rrw .crsl-card-name{transition:none}}

/* ===================================================== book: album + flat ==
   New CSS for the Book block's `scroll-album` and `flat-reading` variants.
   Written to be appended to packages/core/src/blocks/shared.css verbatim,
   after the existing `.bk` rules. Nothing above it changes.

   Colour comes only from --c-* tokens. shared.css has no spacing scale, so
   every length here is a literal or a clamp(), same as the rest of the file.

   The geometry is a MODEL. Every position is derived from a small set of
   custom properties that the component writes (--bk-strips, --bk-arc,
   --bk-aspect, --bk-block-max, --bk-cut-*, --bk-spine), so four spreads and
   forty spreads both come out right. Change the strip count and the arc keeps
   its shape; change the page aspect and the spread follows.
   ========================================================================= */

/* A plain number that can be interpolated. Without @property the bend
   keyframes are inert, --bk-b stays 0, and the stand-in sheet turns flat --
   the page still turns, it just does not curl. That is the whole degradation,
   and it needs no feature test. */
@property --bk-b{syntax:'<number>';inherits:true;initial-value:0}

/* ------------------------------------------------------------ flat reading
   This is also what renders before the script runs and what renders when the
   script never runs: the same markup, unanimated, spreads stacked in reading
   order. A closed book that never opens is content that has gone missing, so
   the flat list is the ground state and the album is layered on top of it.

   Every rule is scoped under .bk-album on purpose. A leaf here is `.bk-folio`
   and not `.bk-leaf` for the same reason: the spread-slider already owns
   `.bk-leaf`, down to :first-child/:last-child gutter shadows, and reusing the
   name would have dressed the album's last leaf in them.
   ------------------------------------------------------------------------ */
.rrw .bk-album{container-type:inline-size}
.rrw .bk-album .bk-runway{position:relative}
.rrw .bk-album .bk-book{display:flex;flex-direction:column;gap:22px;width:min(100%,52rem);margin:0 auto}
.rrw .bk-album .bk-folio{display:flex;flex-direction:column;gap:22px;flex:0 0 auto;aspect-ratio:auto}
.rrw .bk-album .bk-page{position:relative;display:flex;flex-direction:column;gap:12px;
  background:var(--c-bg);border:1px solid var(--c-border);border-radius:var(--c-radius);
  padding:clamp(18px,2.4vw,30px);min-height:12rem}
.rrw .bk-album .bk-page-title{font-size:clamp(17px,1.5vw,21px);color:var(--c-head)}
.rrw .bk-album .bk-page-body{font-size:15px;line-height:1.62;color:var(--c-text)}
.rrw .bk-album .bk-page-caption{font-size:12px;letter-spacing:.04em;color:var(--c-text);opacity:.62}
.rrw .bk-album .bk-page-photo{display:block;width:100%;aspect-ratio:var(--bk-aspect,.72);
  border-radius:calc(var(--c-radius) - 2px)}
.rrw .bk-album .bk-page-cover{background:var(--c-bg-muted);border-color:var(--c-accent)}
.rrw .bk-album .bk-page-cover .bk-page-title{color:var(--c-accent)}
/* The book as an object: thickness, spine, the sheet in flight. None of it
   exists in a stack of sections, so none of it is rendered there either. */
.rrw .bk-album .bk-sheet,.rrw .bk-album .bk-cut,
.rrw .bk-album .bk-spine,.rrw .bk-album .bk-runner{display:none}

.rrw .bk-album .bk-help{display:flex;justify-content:center;padding-top:20px}
.rrw .bk-album .bk-help .cbtn{font-size:12px}

/* ------------------------------------------------------------- album: room
   Not a book against a flat wall in even light. It lies on a table, the light
   comes in from upper left, and the shadow falls down and to the right. The
   light pool and the shadow direction are what make it a room instead of a
   diagram. */
.rrw .bk-album[data-mode="album"] .bk-scene{
  position:sticky;top:var(--bk-top,0px);
  height:calc(100svh - var(--bk-top,0px));
  display:grid;place-items:center;overflow:clip;
  perspective:2600px;
  /* Four degrees of looking-down is enough to put it on a table. More, and
     body text starts running away in the vertical. */
  perspective-origin:50% 32%;
  background:radial-gradient(120% 90% at 22% 8%,
    color-mix(in oklab,var(--c-bg) 70%,var(--c-bg-muted)) 0%,
    var(--c-bg-muted) 46%,
    color-mix(in oklab,var(--c-bg-muted) 84%,var(--c-text)) 100%);
}

/* -------------------------------------------------------- album: the block
   Driven by HEIGHT with width as the stop. The other way round is exactly what
   clips a book at 1440x700, an ordinary laptop. The paper block sits OUTSIDE
   the page box, so the width stop has to reserve room for it -- otherwise the
   scene shears the thickness off at the edge, and the thickness is the thing
   that says "book". --bk-block-max is written by the component out of the same
   number that draws the block, so the two can never drift apart. */
.rrw .bk-album[data-mode="album"] .bk-book{
  --bk-h:min(calc(100svh - var(--bk-top,0px) - 64px),40rem,
             calc((100cqw - 48px - 2 * var(--bk-block-max,28px)) / 2 / var(--bk-aspect,.72)));
  --bk-w:calc(var(--bk-h) * var(--bk-aspect,.72));
  position:relative;display:block;width:calc(var(--bk-w) * 2);height:var(--bk-h);
  margin:0;gap:0;
  transform-style:preserve-3d;
  /* A closed book stands centred: the book BOX is two pages wide, so the right
     half's centre sits a quarter of a box to the right and has to be pulled
     back by exactly that much. */
  translate:0 0;
  rotate:1 0 0 4deg;
  transition:translate .62s cubic-bezier(.2,.7,.25,1);
}
.rrw .bk-album[data-mode="album"][data-state="closed"] .bk-book{translate:-25% 0}
.rrw .bk-album[data-mode="album"][data-state="end"] .bk-book{translate:25% 0}

/* The table under the book. No blur(): a filter is re-rastered every frame as
   soon as anything nearby animates, and a radial gradient is already soft. */
.rrw .bk-album[data-mode="album"] .bk-book::after{
  content:"";position:absolute;inset-inline:6% 0;bottom:-38px;height:54px;
  transform:skewX(9deg);
  background:radial-gradient(52% 100% at 46% 0,
    color-mix(in oklab,var(--c-text) 34%,transparent),
    color-mix(in oklab,var(--c-text) 12%,transparent) 52%,transparent 78%);
  z-index:0;pointer-events:none;
}

/* -------------------------------------------------------------- the cuts
   What makes it a book and not a sheet of paper: the paper block shows as a
   cut outside the pages, and the cut CHANGES as you turn -- the right side
   thins out, the left grows. A book of constant thickness is a prop; the
   change is what says the pages actually moved.

   The widths are computed in the component and written here as px. No
   transition on width: an animated width relays out the page every frame, and
   that is what made the turn stutter. The step happens while a whole sheet
   swings past it, so the jump cannot be seen -- the lag could. */
.rrw .bk-album[data-mode="album"] .bk-cut{
  display:block;position:absolute;inset-block:1px;z-index:1;pointer-events:none;
  /* The hairlines ARE the page edges. The irregular spacing is deliberate: an
     even comb reads as ribbed plastic, three different gaps read as paper. */
  background:repeating-linear-gradient(to right,
    var(--c-bg) 0 1px,
    color-mix(in oklab,var(--c-bg) 78%,var(--c-text)) 1px 2px,
    var(--c-bg) 2px 3px,
    color-mix(in oklab,var(--c-bg) 88%,var(--c-text)) 3px 5px);
}
.rrw .bk-album[data-mode="album"] .bk-cut-r{
  inset-inline-start:100%;width:var(--bk-cut-r,0px);border-radius:0 3px 3px 0;
  box-shadow:2px 0 6px -1px color-mix(in oklab,var(--c-text) 34%,transparent);
}
.rrw .bk-album[data-mode="album"] .bk-cut-l{
  inset-inline-end:100%;width:var(--bk-cut-l,0px);border-radius:3px 0 0 3px;
  box-shadow:-2px 0 6px -1px color-mix(in oklab,var(--c-text) 34%,transparent);
}
/* The spine only shows on a closed book. On an open one it is down in the
   gutter and not something you look at head on. */
.rrw .bk-album[data-mode="album"] .bk-spine{
  display:block;position:absolute;inset-block:0;inset-inline-start:50%;
  width:var(--bk-spine,0px);transform:translateX(-100%);
  background:linear-gradient(to right,
    color-mix(in oklab,var(--c-text) 62%,var(--c-accent)),
    var(--c-accent) 60%,
    color-mix(in oklab,var(--c-text) 72%,var(--c-accent)));
  z-index:1;opacity:0;transition:opacity .4s;pointer-events:none;
}
.rrw .bk-album[data-mode="album"][data-state="closed"] .bk-spine,
.rrw .bk-album[data-mode="album"][data-state="end"] .bk-spine{opacity:1}
/* Turned all the way through, the block sits on the other side of the spine. */
.rrw .bk-album[data-mode="album"][data-state="end"] .bk-spine{transform:none}

/* --------------------------------------------------------------- the leaves
   Each leaf is real paper: the front is a spread's right-hand page, the back
   is the next spread's left-hand page. That is why the next text comes AT you
   during the turn instead of fading in.

   THE TURN IS A FALL, not an even rotation. A symmetric curve gives the same
   speed up as down, and that is exactly what reads as stiff: paper is lifted,
   is slowest on edge, then falls with gravity and settles. The stops below are
   that physics and they are interpolated LINEARLY so the shape lives in the
   stops and nowhere else -- 1.25 deg per percent around vertical against 3.0
   on the way down, so the fall is 2.4x the lift, plus three degrees of
   overshoot that settles at the end. */
@keyframes bk-fall{
    0%{transform:rotateY(0deg)}
   15%{transform:rotateY(-25deg)}
   40%{transform:rotateY(-70deg)}
   60%{transform:rotateY(-95deg)}
   75%{transform:rotateY(-140deg)}
   88%{transform:rotateY(-176deg)}
   95%{transform:rotateY(-183deg)}
  100%{transform:rotateY(-180deg)}
}
@keyframes bk-fall-back{
    0%{transform:rotateY(-180deg)}
   15%{transform:rotateY(-155deg)}
   40%{transform:rotateY(-110deg)}
   60%{transform:rotateY(-85deg)}
   75%{transform:rotateY(-40deg)}
   88%{transform:rotateY(-4deg)}
   95%{transform:rotateY(3deg)}
  100%{transform:rotateY(0deg)}
}
/* The bend shares its stops with the rotation, in the same percentages of the
   flight. Paper is at its most curved just around vertical -- that is where
   the deepest bow sits in real footage -- and flat at both ends. Letting the
   two run on separate curves was the original mistake: the paper was still
   bent after the page had already come to rest. */
@keyframes bk-bend{
    0%{--bk-b:0}
   15%{--bk-b:.5}
   40%{--bk-b:.92}
   60%{--bk-b:1}
   75%{--bk-b:.7}
   88%{--bk-b:.25}
   95%{--bk-b:.06}
  100%{--bk-b:0}
}
/* While the sheet is in flight the stand-in is what you see. The leaf's own
   faces are hidden, or the text shines through between the strips and the
   sheet goes see-through -- which reads as broken, not as soft. */
@keyframes bk-sheet-on{0%,99%{opacity:1}100%{opacity:0}}
@keyframes bk-face-off{0%,99%{visibility:hidden}100%{visibility:visible}}
/* The runners: when a sheet lifts, its neighbours move. */
@keyframes bk-stir{0%{opacity:0}12%{opacity:.75}88%{opacity:.75}100%{opacity:0}}

.rrw .bk-album[data-mode="album"] .bk-folio{
  position:absolute;inset-block:0;inset-inline-start:50%;width:50%;
  display:block;gap:0;
  transform-origin:left center;transform-style:preserve-3d;
  transform:rotateY(0deg);
  transition:transform .66s cubic-bezier(.34,.02,.2,1);
  z-index:calc(90 - var(--bk-i,0));
  animation-duration:var(--bk-turn,720ms);
  animation-delay:var(--bk-delay,0ms);
  animation-timing-function:linear;
  animation-fill-mode:both;
}
.rrw .bk-album[data-mode="album"] .bk-folio[data-turned="true"]{
  transform:rotateY(-180deg);z-index:calc(10 + var(--bk-i,0));
}
/* The active leaf goes on top whether turned or not. It only matters on a
   narrow frame, where one page shows at a time and the back you just turned
   otherwise lands under the next leaf's front -- which skipped every text page
   outright. On a wide frame both halves show anyway and this changes nothing.

   A leaf only ever alternates between fwd and back, so the animation-name
   always differs from the one before it and the turn restarts by itself. No
   timer clears it, and fill-mode:both parks it on an end state that matches
   the resting transform above. */
.rrw .bk-album[data-mode="album"] .bk-folio[data-active]{z-index:200}
.rrw .bk-album[data-mode="album"] .bk-folio[data-fly="fwd"]{animation-name:bk-fall,bk-bend}
.rrw .bk-album[data-mode="album"] .bk-folio[data-fly="back"]{animation-name:bk-fall-back,bk-bend}
/* Only the leaf that is actually flying gets promoted to its own layer.
   will-change on every leaf all the time is memory held for nothing. */
.rrw .bk-album[data-mode="album"] .bk-folio[data-fly]{will-change:transform}

.rrw .bk-album[data-mode="album"] .bk-page{
  position:absolute;inset:0;backface-visibility:hidden;overflow:clip;
  background:var(--c-bg);border:0;border-radius:0;min-height:0;
  justify-content:center;
  padding:clamp(14px,2.2cqw,30px);
  animation-duration:var(--bk-turn,720ms);
  animation-delay:var(--bk-delay,0ms);
  animation-timing-function:linear;
  animation-fill-mode:both;
}
.rrw .bk-album[data-mode="album"] .bk-folio[data-fly] .bk-page{animation-name:bk-face-off}
.rrw .bk-album[data-mode="album"] .bk-page-back{transform:rotateY(180deg)}
/* Text does NOT shrink. A flex child that quietly compresses turns a page that
   does not fit into a page that merely looks tight, and the fit test below
   would then measure a lie. The photograph is the only thing allowed to give. */
.rrw .bk-album[data-mode="album"] .bk-page-title,
.rrw .bk-album[data-mode="album"] .bk-page-body,
.rrw .bk-album[data-mode="album"] .bk-page-caption{flex:0 0 auto}
.rrw .bk-album[data-mode="album"] .bk-page-photo{flex:1 1 auto;min-height:0;height:auto}
.rrw .bk-album[data-mode="album"] .bk-page-cover{
  background:var(--c-bg-muted);border:0;
  align-items:center;text-align:center;
}
/* The gutter shadow. The right page darkens towards its left edge, the left
   page towards its right -- the only difference that makes two flat surfaces
   read as one spread instead of two cards. */
.rrw .bk-album[data-mode="album"] .bk-page::before{
  content:"";position:absolute;inset-block:0;width:12%;pointer-events:none;z-index:2;
}
.rrw .bk-album[data-mode="album"] .bk-page-front::before{
  inset-inline-start:0;
  background:linear-gradient(to right,color-mix(in oklab,var(--c-text) 26%,transparent),transparent);
}
.rrw .bk-album[data-mode="album"] .bk-page-back::before{
  inset-inline-end:0;
  background:linear-gradient(to left,color-mix(in oklab,var(--c-text) 26%,transparent),transparent);
}

/* ----------------------------------------------------- the sheet in flight
   A sheet in the air is a CONE, not a plane. It is held at the spine, the free
   edge swings out, and the sheet bends so that its top and bottom edges pull
   in. A bending leaf cannot be one CSS surface, so during the turn a STAND-IN
   is laid over it: same paper, same frame, no body text, built from strips
   with an angle each. The real page lands sharp when the turn is done. In real
   footage the text on a flying sheet is unreadable too -- a sheet in motion is
   a shape, not a text.

   The strips sit in a CHAIN, not side by side. Each one sits at its parent's
   right edge and rotates about its own left edge, so the angles compose and
   the surface becomes an arc. Side by side, each rotating on its own, they
   open like a fan instead.

   --bk-arc is the TOTAL turn across the chain and --bk-strips is how many
   links share it, so the shape is independent of the strip count. */
.rrw .bk-album[data-mode="album"] .bk-sheet{
  display:block;position:absolute;inset:0;transform-style:preserve-3d;
  opacity:0;pointer-events:none;z-index:3;
  animation-duration:var(--bk-turn,720ms);
  animation-delay:var(--bk-delay,0ms);
  animation-timing-function:linear;
  animation-fill-mode:both;
}
.rrw .bk-album[data-mode="album"] .bk-folio[data-fly] .bk-sheet{animation-name:bk-sheet-on}
.rrw .bk-album[data-mode="album"] .bk-sheet i{
  position:absolute;inset-block:0;inset-inline-start:0;
  width:calc(100% / var(--bk-strips,8));
  transform-origin:left center;transform-style:preserve-3d;
  transform:rotateY(calc(var(--bk-b,0) * var(--bk-arc,27) / var(--bk-strips,8) * -1deg));
  background:var(--c-bg);
}
.rrw .bk-album[data-mode="album"] .bk-sheet i i{inset-inline-start:100%;width:100%}
/* The light rakes along the arc: a trough at the spine, a ridge further out.
   The gradient is STATIC and only the opacity follows the bend. Putting --bk-b
   inside the colour stops looked tidier and forced every strip to re-raster
   every frame -- eight strips times the sheets in the air. Opacity composites
   instead, and the motion got smooth on that one line. */
.rrw .bk-album[data-mode="album"] .bk-sheet i::before{
  content:"";position:absolute;inset:0;opacity:var(--bk-b,0);
  background:linear-gradient(to right,
    color-mix(in oklab,var(--c-text) 16%,transparent),
    color-mix(in oklab,var(--c-text) 2%,transparent));
}

/* The runners. A NARROW strip at the spine, not a whole page -- a half page
   laid over the spread whitewashes the page that is about to land. Their
   angles are one ramp off --bk-runner-deg, so adding a third runner does not
   need a third hand-picked number. */
.rrw .bk-album[data-mode="album"] .bk-runner{
  display:block;position:absolute;inset-block:4%;inset-inline-start:50%;width:14%;
  transform-origin:left center;
  background:linear-gradient(to right,
    color-mix(in oklab,var(--c-bg) 90%,var(--c-text)),var(--c-bg));
  border-radius:0 3px 3px 0;
  opacity:0;z-index:2;pointer-events:none;
  transform:rotateY(calc(var(--bk-b,0) * var(--bk-v,0) * 1deg));
  /* The runners are siblings of the leaves, so the bend animated on a flying
     leaf never reaches them. They run their own copy of it, restarted by the
     component remounting them on each flight. */
  animation:bk-stir var(--bk-turn,720ms) linear both,
            bk-bend var(--bk-turn,720ms) linear both;
}
.rrw .bk-album[data-mode="album"][data-narrow="true"] .bk-runner{inset-inline-start:0;width:16%}

/* ------------------------------------------------------------ narrow frames
   An open book is two pages side by side and cannot be that at 375px. Here the
   leaf IS the whole book: one page at a time, the same turn, no sideways
   scroll. The reading order becomes text, photo, text, photo -- which is the
   order the pages sit in in the paper book.

   Narrow is measured off the block's own width, not the viewport: the
   component library renders the same block in a 390px frame inside a wide
   window, and a viewport media query is blind to that. */
.rrw .bk-album[data-mode="album"][data-narrow="true"] .bk-book{
  --bk-w:min(calc(100cqw - 24px - 2 * var(--bk-block-max,28px)),26rem);
  /* Portrait here, not the book's own aspect. The aspect is the format in the
     hand; at 320px of width a page at that ratio cannot hold a page of text at
     a readable size, and overflow:clip DELETES the last lines instead of
     growing. The page takes all the height it can get instead, and the fit
     test below is what decides whether that was enough. */
  --bk-h:min(calc(100svh - var(--bk-top,0px) - 48px),
             calc(var(--bk-w) * 2 / var(--bk-aspect,.72)));
  width:var(--bk-w);
}
.rrw .bk-album[data-mode="album"][data-narrow="true"] .bk-folio{inset-inline-start:0;width:100%}
/* Its own keyframes: the leaf also carries a translateX. Same stops, same
   physics, with the displacement baked in. A leaf that is the whole book turns
   about its left edge and lands OUTSIDE the book to the left, showing the next
   untouched leaf instead; the translation carries it back to its own place.
   The order matters -- the rotation runs first, so it is written last. */
@keyframes bk-fall-n{
    0%{transform:translateX(0) rotateY(0deg)}
   15%{transform:translateX(13%) rotateY(-25deg)}
   40%{transform:translateX(37%) rotateY(-70deg)}
   60%{transform:translateX(53%) rotateY(-95deg)}
   75%{transform:translateX(78%) rotateY(-140deg)}
   88%{transform:translateX(98%) rotateY(-176deg)}
   95%{transform:translateX(102%) rotateY(-183deg)}
  100%{transform:translateX(100%) rotateY(-180deg)}
}
@keyframes bk-fall-n-back{
    0%{transform:translateX(100%) rotateY(-180deg)}
   15%{transform:translateX(98%) rotateY(-155deg)}
   40%{transform:translateX(78%) rotateY(-110deg)}
   60%{transform:translateX(53%) rotateY(-85deg)}
   75%{transform:translateX(37%) rotateY(-40deg)}
   88%{transform:translateX(13%) rotateY(-4deg)}
   95%{transform:translateX(-2%) rotateY(3deg)}
  100%{transform:translateX(0) rotateY(0deg)}
}
.rrw .bk-album[data-mode="album"][data-narrow="true"] .bk-folio[data-fly="fwd"]{
  animation-name:bk-fall-n,bk-bend;
}
.rrw .bk-album[data-mode="album"][data-narrow="true"] .bk-folio[data-fly="back"]{
  animation-name:bk-fall-n-back,bk-bend;
}
.rrw .bk-album[data-mode="album"][data-narrow="true"] .bk-folio[data-turned="true"]{
  transform:translateX(100%) rotateY(-180deg);
}
/* With preserve-3d, siblings sort on their depth and not on z-index, so four
   pixels forward is what actually puts the active page on top. At 2600px of
   perspective there is no visible change of scale. */
.rrw .bk-album[data-mode="album"][data-narrow="true"] .bk-folio[data-active]{transform:translateZ(4px)}
.rrw .bk-album[data-mode="album"][data-narrow="true"] .bk-folio[data-active][data-turned="true"]{
  transform:translateZ(4px) translateX(100%) rotateY(-180deg);
}
.rrw .bk-album[data-mode="album"][data-narrow="true"][data-state="closed"] .bk-book,
.rrw .bk-album[data-mode="album"][data-narrow="true"][data-state="end"] .bk-book{translate:0 0}
.rrw .bk-album[data-mode="album"][data-narrow="true"] .bk-page::before{content:none}
.rrw .bk-album[data-mode="album"][data-narrow="true"] .bk-page{padding:22px 19px}
.rrw .bk-album[data-mode="album"][data-narrow="true"] .bk-page-title{font-size:1.35rem}
.rrw .bk-album[data-mode="album"][data-narrow="true"] .bk-page-body{font-size:1.0625rem;line-height:1.55}

/* --------------------------------------------------------------- measuring
   The fit test runs with the album's own page box in place but every 3D
   transform switched off, so a child's rectangle can be compared against its
   page's content box without perspective skewing the numbers. It is set and
   cleared inside one frame, so it never paints. */
.rrw .bk-album[data-measuring] .bk-scene{perspective:none}
.rrw .bk-album[data-measuring] .bk-folio{transform:none;transition:none;animation:none}
.rrw .bk-album[data-measuring] .bk-page{
  transform:none;animation:none;visibility:visible;backface-visibility:visible;
}
.rrw .bk-album[data-measuring] .bk-sheet{display:none}

/* Belt and braces. The component already forces the flat list under reduced
   motion; this makes the same promise for anything that reaches the CSS
   without the script. */
@media (prefers-reduced-motion:reduce){
  .rrw .bk-album .bk-book,.rrw .bk-album .bk-folio,.rrw .bk-album .bk-page,
  .rrw .bk-album .bk-sheet,.rrw .bk-album .bk-runner{
    animation:none!important;transition:none!important;
  }}

/* photostrip sist: några regler överlagrar .ph */
/* ── PhotoStrip: polaroid mechanics (deck / conveyor / line) ──────────────────
   APPEND this to shared.css, after the existing rules: a handful of selectors
   here override .ph, and they rely on coming later at equal specificity.

   THE MODEL, not the numbers. Every position is a formula over --pol-*: the
   stack step and the pitch derive from the card width, the tilt derives from
   the card's place in the count, and the lap derives from the count as well. A
   strip of four and a strip of nine both land right, at any frame width, with
   nothing to retune. The variables worth touching per site are the five at the
   top of .pol; everything below is arithmetic on them.

   Colour is tokens only. The paper is --c-bg with a --c-border edge, so the
   polaroid reads as paper on both the plain and the muted band, and the peg and
   the hairline take --c-accent. */

.rrw .pol{
  /* the four you tune */
  --pol-w:min(22rem,74vw);          /* card width; everything else follows it */
  --pol-pad:12px;                   /* paper margin; the chin is a multiple of it */
  --pol-gap:clamp(14px,2.4vw,28px); /* between cards on the rail */
  --pol-tilt:.9deg;                 /* tilt STEP per card, middle card straight */

  /* arithmetic */
  --pol-n:6;                        /* card count, written by the component */
  --pol-step-x:calc(var(--pol-w) * .055);
  --pol-step-y:calc(var(--pol-w) * .04);
  --pol-lift:clamp(1.25rem,3.5vw,3rem);   /* slack for rotation and shadow */
  --pol-develop:900ms;
  --pol-fog:.55;
  --pol-deck-tilt:2.4deg;           /* tilt step through the depth of the pile */
  --pol-deck-lean:2deg;             /* the whole pile leans this far */
  --pol-fly-x:124%;                 /* how far a flicked card travels */
  --pol-fly-r:11deg;
  --pol-z:20;                       /* the top card's layer; depth counts down */
}

/* the tilt every variant shares: distance from the middle of the count, times
   the step. (n - 1) / 2 is the middle — the source's hand-written 2.5 for six
   cards, written as the formula it was. */
.rrw .pol-card{
  margin:0;
  background:var(--c-bg);
  border:1px solid var(--c-border);
  padding:var(--pol-pad) var(--pol-pad) calc(var(--pol-pad) * 2.4);
  box-shadow:0 1px 2px rgba(0,0,0,.18),0 14px 24px -14px rgba(0,0,0,.45);
}
.rrw .pol-fig{margin:0}
.rrw .pol-frame{position:relative;display:block;aspect-ratio:4 / 5;overflow:hidden}
.rrw .pol-frame .pol-shot{position:absolute;inset:0;background-size:cover;background-position:center}
.rrw .pol-haze{position:absolute;inset:0;pointer-events:none;opacity:0;
  background:linear-gradient(180deg,
    color-mix(in oklab,var(--c-bg) 62%,#fff),
    color-mix(in oklab,var(--c-bg-muted) 78%,#fff))}
.rrw .pol-cap{margin:0;padding:calc(var(--pol-pad) * .9) 2px 0;display:grid;gap:2px}
.rrw .pol-name{font-family:var(--c-hand);font-size:22px;line-height:1.05;color:var(--c-head)}
.rrw .pol-note{font-size:12.5px;line-height:1.35;color:var(--c-text);opacity:.66}

/* THE DEVELOP. A real polaroid comes up out of a milky, almost empty frame.
   Filters only: no second copy of the picture, nothing to preload. It is bound
   to the attribute that marks "this one is in your hand", so it replays by
   itself every time a new card takes that place. */
@keyframes rrw-pol-develop{
  0%  {opacity:.10;filter:blur(7px) contrast(.35) saturate(.15) brightness(1.4)}
  35% {opacity:.55;filter:blur(3px) contrast(.7) saturate(.55) brightness(1.16)}
  70% {opacity:.88;filter:blur(.7px) contrast(.92) saturate(.88) brightness(1.04)}
  100%{opacity:1;filter:none}
}
@keyframes rrw-pol-haze{0%{opacity:.9}60%{opacity:.25}100%{opacity:0}}
.rrw .pol-card[data-front="true"] .pol-shot{animation:rrw-pol-develop var(--pol-develop) ease-out both}
.rrw .pol-card[data-front="true"] .pol-haze{animation:rrw-pol-haze var(--pol-develop) ease-out both}

/* THE QUEUE IS UNDEVELOPED (deck and line). A photograph still in the pile has
   not come up yet, so the card that arrives is CONTINUING from the state it was
   already in, instead of flashing back to milky in the middle of the gesture. */
.rrw .pol-v-deck .pol-card:not([data-front="true"]) .pol-shot{
  filter:blur(2.5px) contrast(.62) saturate(.4) brightness(1.18);transition:filter 520ms ease}
.rrw .pol-v-deck .pol-card:not([data-front="true"]) .pol-haze{
  opacity:var(--pol-fog);transition:opacity 520ms ease}

/* ── Controls ──
   The pause button is not optional: content that moves for more than five
   seconds has to be stoppable. The hairline says how long the current card has
   left, written from the same clock that advances the cards. */
.rrw .pol-controls{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin:0 0 18px}
.rrw .pol-btn{min-width:44px;min-height:44px;display:inline-flex;align-items:center;justify-content:center;
  font-size:15px;font-family:inherit;background:transparent;color:var(--c-text);
  border:1px solid var(--c-border);border-radius:var(--c-radius);cursor:pointer;transition:background .16s,color .16s,border-color .16s}
.rrw .pol-btn:hover{background:var(--c-accent);color:var(--c-on-accent);border-color:var(--c-accent)}
.rrw .pol-count{font-size:12px;letter-spacing:.14em;font-variant-numeric:tabular-nums;color:var(--c-text);opacity:.6}
.rrw .pol-meter{flex:1 1 6rem;min-width:4rem;height:2px;background:color-mix(in oklab,var(--c-text) 18%,transparent)}
.rrw .pol-meter i{display:block;height:100%;background:var(--c-accent);transform-origin:left;transform:scaleX(var(--t,0))}
.rrw .pol[data-still="true"] .pol-meter i{opacity:.35}

/* ── A. The deck ──
   The scene is a grid with every card in the same cell, so its height is the
   tallest card's own height: the chin is measured from the content instead of
   being declared in rem, which is what broke when the frame moved. The padding
   is the slack a rotated card and its shadow need. */
.rrw .pol-v-deck .pol-stage{position:relative;display:grid;justify-content:center;
  padding:var(--pol-lift);touch-action:pan-y;overflow:clip;overflow-clip-margin:var(--pol-lift)}
.rrw .pol-v-deck .pol-stage:focus-visible{outline:2px solid var(--c-accent);outline-offset:4px}
.rrw .pol-pile{list-style:none;margin:0;padding:0;display:grid;grid-template-areas:"pile"}
.rrw .pol-v-deck .pol-card{grid-area:pile;width:var(--pol-w);
  /* NO scale in the depth transform: with the origin near the thumb a small
     shrink pulls the top edge down about as far as the offset pushes it out,
     and the pile cancels itself into one photograph. Translation and rotation
     only. The drag tilt has its own UNITLESS variable, because px inside a deg
     calc is invalid and an invalid calc drops the whole transform. */
  transform:
    translate(calc(var(--d,0) * var(--pol-step-x) + var(--dx,0px)),
              calc(var(--d,0) * var(--pol-step-y) * -1))
    rotate(calc(var(--d,0) * var(--pol-deck-tilt) - var(--pol-deck-lean) + var(--dr,0) * 1deg));
  transform-origin:50% 92%;
  z-index:calc(var(--pol-z) - var(--d,0));
  transition:transform 480ms cubic-bezier(.22,.61,.36,1),opacity 320ms ease}
/* The ones nobody sees park exactly UNDER the top card, hidden by OCCLUSION
   rather than by opacity: an opacity-0 box still counts in overflow, and fading
   a card fades its caption to text nobody can read. */
.rrw .pol-v-deck .pol-card[data-buried="true"]{pointer-events:none;
  transform:translate(0,0) rotate(calc(var(--pol-deck-lean) * -1))}
/* Off to the side and barely up, the way a photo slides off a pile rather than
   being thrown. Above the whole pile: the order advances at the START of the
   flick, so without this the leaving card would slide out behind the pile. A
   flick STARTS fast, so ease-out — an ease-in curve leaves two captions lying
   on top of each other at 150ms, which reads as a smudge. */
.rrw .pol-v-deck .pol-card[data-flying="true"]{z-index:calc(var(--pol-z) + 10);opacity:0;
  transform:translate(var(--pol-fly-x),5%) rotate(var(--pol-fly-r));
  transition:transform 420ms cubic-bezier(.2,.8,.3,1),opacity 360ms ease-out}
.rrw .pol-v-deck .pol-card[data-frozen="true"]{transition:none}

/* ── Mobile ── */
.rrw.mobile .pol{--pol-w:min(15rem,70vw);--pol-pad:10px;--pol-lift:1.25rem}
.rrw.mobile .pol-name{font-size:19px}

/* ── No motion ──
   The fallback is the same in all three: no pile, no rail, no drift. Every
   photograph laid out, all of the content and none of the movement. !important
   because it has to beat the per-variant rules above, which are more specific
   by design. */
@media (prefers-reduced-motion:reduce){
  /* display:block, not just static: the deck's stage is a centred grid, and a
     laid-out grid inside a centred track collapses to one column. */
  .rrw .pol-stage,.rrw .pol-view{display:block!important;position:static!important;
    overflow:visible!important;padding:0!important}
  .rrw .pol-pile,.rrw .pol-rail{display:grid!important;width:auto!important;gap:var(--pol-gap)!important;
    grid-template-columns:repeat(auto-fit,minmax(min(15rem,100%),1fr))!important;
    transform:none!important;animation:none!important;transition:none!important}
  .rrw .pol-card{position:static!important;grid-area:auto!important;width:auto!important;opacity:1!important;
    transform:rotate(calc((var(--i) - (var(--pol-n) - 1) / 2) * var(--pol-tilt)))!important;
    rotate:0deg!important;transition:none!important;pointer-events:auto!important}
  .rrw .pol-card .pol-shot{filter:none!important;opacity:1!important;animation:none!important}
  .rrw .pol-card .pol-haze{opacity:0!important;animation:none!important}
  .rrw .pol-controls{display:none!important}}
/* ═══ Omgång 2, 2026-08-17 ═══ */

/* ── Marquee: a running text band ─────────────────────────────────────────────
   Append to shared.css. Nothing here overlaps the existing `.marq` rules — that
   is the picture marquee in the photo strip; this is the text one, prefixed .mq

   One mechanic for all three variants: the phrase list is written twice, the
   track is width:max-content (so it is exactly two laps wide), and pulling it to
   translateX(-50%) lands on the start of the second lap. The seam is invisible
   because it is the same run of words.

   Design-agnostic: every colour is a --c-* token. The sources' hard-coded red is
   var(--c-accent), their near-black separator is var(--c-bg). */

@keyframes rrmq{from{transform:translateX(0)}to{transform:translateX(-50%)}}

.rrw .mq{
  /* The only duration in the file. Variants do not restate it; a caller that
     wants another pace sets --mq-speed from data. */
  --mq-speed:34s;
  --mq-pad:.9rem;
  overflow:hidden;
  background:var(--c-bg-muted);
  border-block:1px solid var(--c-border);
  padding-block:var(--mq-pad);
}

.rrw .mq-track{
  display:flex;
  width:max-content;
  will-change:transform;
  animation:rrmq var(--mq-speed) linear infinite;
}

/* Hover pauses the band, and so does keyboard focus landing inside it. Only one
   of the three sources paused at all, and none of them paused for the keyboard:
   a reader who cannot use a mouse otherwise has no way to hold a phrase still. */
.rrw .mq:hover .mq-track,
.rrw .mq:focus-within .mq-track{animation-play-state:paused}

.rrw .mq-copy{display:flex;align-items:center;flex-shrink:0}

.rrw .mq-word,
.rrw .mq-sep{
  font-family:var(--c-heading);
  font-weight:600;
  text-transform:uppercase;
  letter-spacing:.04em;
  line-height:1.15;
  white-space:nowrap;
  padding-inline:.55rem;
}
.rrw .mq-word{font-size:clamp(1.25rem,3.2vw,2.3rem);color:var(--c-head)}
.rrw .mq-sep{font-size:clamp(.8rem,2vw,1.5rem);color:var(--c-accent)}

/* ── dark-band: muted ground, the marked phrase lit ── */
.rrw .mq--dark .mq-word--em{
  color:var(--c-accent);
  text-shadow:0 0 .55em color-mix(in oklab,var(--c-accent) 55%,transparent);
}

/* ── outline-band: the marked phrase hollowed out ──
   The plain fallback comes first and is unconditional. An engine without
   text-stroke would otherwise render color:transparent and the marked phrases
   would simply be missing, which is what the source risks. */
.rrw .mq--outline .mq-word--em{color:var(--c-accent)}
@supports ((-webkit-text-stroke:1px currentColor) or (text-stroke:1px currentColor)){
  .rrw .mq--outline .mq-word--em{
    color:transparent;
    -webkit-text-stroke:1.4px var(--c-accent);
    text-stroke:1.4px var(--c-accent);
  }
}

/* ── brand-band: solid accent ground, tighter and read as a banner ── */
.rrw .mq--brand{
  --mq-pad:.55rem;
  background:var(--c-accent);
  border-block-color:var(--c-accent);
}
.rrw .mq--brand .mq-word{color:var(--c-on-accent)}
.rrw .mq--brand .mq-word--em{color:var(--c-bg)}
.rrw .mq--brand .mq-sep{color:var(--c-bg);opacity:.7}

/* ── Phone frame ──
   The clamps read vw, and vw is the viewport, not the 390px preview frame, so
   the band would set far too large inside .rrw.mobile. */
.rrw.mobile .mq-word{font-size:1.05rem}
.rrw.mobile .mq-sep{font-size:.7rem}
.rrw.mobile .mq{--mq-pad:.6rem}

/* ── Reduced motion ──
   Stopping the animation is not enough on its own: the track is max-content
   inside overflow:hidden, so a stopped band shows its first screenful and clips
   the rest. Two of the three sources stop it exactly that way, and so does the
   Ribbon in template-stacks. Here the band stops being a track and becomes a
   still, centred, wrapping line — the whole phrase list stays readable — and the
   duplicate lap is removed so nothing appears twice. */
@media (prefers-reduced-motion:reduce){
  .rrw .mq-track{
    animation:none;
    width:auto;
    flex-wrap:wrap;
    justify-content:center;
  }
  .rrw .mq-copy{flex-wrap:wrap;justify-content:center;flex-shrink:1}
  .rrw .mq-copy--dup{display:none}
}

/* ── Newsletter block ─────────────────────────────────────────────────────────
   Append to shared.css as-is. Nothing here overrides an existing rule.

   Prefix is nws-, not nl-: shared.css already owns .nl-panel / .nl-field /
   .nl-btn for the footer's newsletter-focused variant, which is a static mock
   (a span where the input belongs, an <a> for the button). Reusing that prefix
   would have the real form inherit a pill built for a fake one.

   Colour rules: the small type uses color:inherit, not var(--c-text), so the
   band works unchanged inside .ftr, where the ground is var(--c-text) and the
   type is var(--c-bg). The input keeps its own light ground on purpose, on
   either background, because a field has to read as a field.

   No danger token exists in the palette, so the error state is var(--c-accent)
   plus weight rather than an invented red. */

.rrw .nws{position:relative}

/* form ------------------------------------------------------------------- */
.rrw .nws-form{display:flex;flex-direction:column;gap:10px;width:100%;min-width:0}
.rrw .nws-row{display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap}
.rrw .nws-form[data-layout="stack"] .nws-row{flex-direction:column;align-items:stretch;flex-wrap:nowrap}
/* min-width:0 or the field refuses to shrink below its input's intrinsic width
   and pushes the button out of the strip instead of narrowing. */
.rrw .nws-field{display:flex;flex-direction:column;gap:6px;flex:1 1 220px;min-width:0}
.rrw .nws-label{font-size:11px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:inherit;opacity:.7}
.rrw .nws-input{width:100%;min-width:0;font:inherit;font-size:14px;line-height:1.4;color:var(--c-text);background:var(--c-bg);border:1px solid var(--c-border);border-radius:var(--c-radius);padding:12px 14px;transition:border-color .16s,box-shadow .16s}
.rrw .nws-input::placeholder{color:var(--c-text);opacity:.42}
.rrw .nws-input:focus-visible{outline:none;border-color:var(--c-accent);box-shadow:0 0 0 3px color-mix(in oklab,var(--c-accent) 26%,transparent)}
.rrw .nws-input:disabled{opacity:.6}
.rrw .nws-form[data-layout="row"] .nws-submit{flex:0 0 auto}
.rrw .nws-form[data-layout="stack"] .nws-submit{align-self:flex-start}
.rrw .nws-submit:disabled{opacity:.62;cursor:progress}

/* the label that is there for readers only */
.rrw .nws-vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}

/* honeypot: off-screen, not display:none, which plenty of bots skip */
.rrw .nws-hp{position:absolute;left:-9999px;top:0;width:1px;height:1px;overflow:hidden}

/* consent, fine print, status -------------------------------------------- */
.rrw .nws-consent{display:flex;align-items:flex-start;gap:9px;font-size:12.5px;line-height:1.5;color:inherit;opacity:.8}
.rrw .nws-consent input{flex:0 0 auto;width:15px;height:15px;margin:2px 0 0;accent-color:var(--c-accent)}
.rrw .nws-consent label{cursor:pointer}
.rrw .nws-fine{font-size:11.5px;line-height:1.5;color:inherit;opacity:.62;margin:0;max-width:52ch}
.rrw .nws-status{font-size:12.5px;line-height:1.5;color:inherit;opacity:.85;margin:0;min-height:1.25em}
.rrw .nws-status:empty{min-height:0}
.rrw .nws-status[data-kind="ok"]{opacity:1;font-weight:600}
.rrw .nws-status[data-kind="bad"]{color:var(--c-accent);opacity:1;font-weight:600}
.rrw .nws-status[data-kind="note"]{opacity:.9}
.rrw .nws-status[data-kind="busy"]{opacity:1}
.rrw .nws-status[data-kind="busy"]::after{content:'';display:inline-block;width:5px;height:5px;margin-left:7px;vertical-align:middle;border-radius:50%;background:currentColor;animation:nws-pulse 1.1s ease-in-out infinite}
@keyframes nws-pulse{0%,100%{opacity:.22}50%{opacity:1}}

/* inline-strip ------------------------------------------------------------ */
.rrw .nws-strip{display:flex;align-items:center;gap:clamp(20px,4vw,52px);padding:clamp(20px,2.6vw,32px) clamp(28px,5vw,56px);background:var(--c-bg-muted);border-top:1px solid var(--c-border);border-bottom:1px solid var(--c-border)}
.rrw .nws-strip .nws-copy{flex:1 1 260px;min-width:0}
.rrw .nws-strip .nws-form{flex:1 1 420px;max-width:520px}
.rrw .nws-strip-eyebrow{margin-bottom:6px}
.rrw .nws-strip-h{font-size:clamp(17px,1.8vw,22px);margin:0}
.rrw .nws-strip-sub{font-size:13px;color:var(--c-text);opacity:.7;margin:5px 0 0;max-width:48ch}
.rrw.mobile .nws-strip{flex-direction:column;align-items:stretch;gap:14px}
.rrw.mobile .nws-strip .nws-form{max-width:none}

/* split-image ------------------------------------------------------------- */
.rrw .nws-split{display:flex;gap:clamp(24px,4vw,48px);align-items:center}
.rrw .nws-split-shot{flex:1 1 0;min-width:0;align-self:stretch;min-height:320px;border-radius:var(--c-radius)}
.rrw .nws-split-words{flex:1 1 0;min-width:0}
.rrw .nws-split-words .lead{margin-bottom:20px}
.rrw .nws-split-words .nws-form{margin-top:18px}
.rrw.mobile .nws-split{flex-direction:column;align-items:stretch;gap:20px}
.rrw.mobile .nws-split-shot{min-height:200px;flex:none}

/* footer-band ------------------------------------------------------------- */
.rrw .nws-band{display:flex;align-items:center;gap:clamp(14px,2.8vw,34px);padding:14px clamp(28px,5vw,56px) 18px;border-bottom:1px solid var(--c-border)}
.rrw .nws-band .nws-copy{flex:1 1 220px;min-width:0}
.rrw .nws-band-h{font-size:15px;line-height:1.2;margin:0}
.rrw .nws-band-sub{font-size:12.5px;line-height:1.45;color:inherit;opacity:.7;margin:3px 0 0;max-width:44ch}
.rrw .nws-band .nws-form{flex:1 1 360px;max-width:420px;gap:7px}
.rrw .nws-band .nws-input{padding:9px 12px;font-size:13px}
.rrw .nws-band .cbtn{padding:10px 18px;font-size:12px}
.rrw .nws-band .nws-consent,.rrw .nws-band .nws-status,.rrw .nws-band .nws-fine{font-size:11.5px}
/* inside a footer the ground is var(--c-text): heading and hairline follow it */
.rrw .ftr .nws-band{border-bottom-color:color-mix(in oklab,var(--c-bg) 22%,transparent)}
.rrw .ftr .nws-band-h{color:var(--c-bg)}
.rrw .ftr .nws-band .nws-input{color:var(--c-text)}
.rrw.mobile .nws-band{flex-direction:column;align-items:stretch;gap:12px}
.rrw.mobile .nws-band .nws-form{max-width:none}

/* stacked-centered -------------------------------------------------------- */
.rrw .nws-stack{text-align:center}
.rrw .nws-stack .eyebrow,.rrw .nws-stack .sec-h{text-align:center}
.rrw .nws-stack .lead{margin:0 auto}
.rrw .nws-stack .nws-form{max-width:480px;margin:24px auto 0;text-align:left}
/* full-width button here, so the attribute selector above is matched too:
   .rrw .nws-stack .nws-submit alone loses to .rrw .nws-form[data-layout] .nws-submit */
.rrw .nws-stack .nws-form[data-layout="stack"] .nws-submit{align-self:stretch}

/* motion ------------------------------------------------------------------ */
@media (prefers-reduced-motion:reduce){
  .rrw .nws-status[data-kind="busy"]::after{animation:none;opacity:.7}
  .rrw .nws-input{transition:none}}

/* ── Reviews / rating-histogram ───────────────────────────────────────────────
   Append to shared.css. Only --c-* tokens; shared.css has no --sp-* scale, so
   sizes are literals in the same style as the rest of the sheet.
   Ported from restaurang-moldau (tools/sida_start.py, the "hallen" band):
   the drawn star row with a clipped second copy, and the distribution bars. */

/* screen-reader-only text (bars are pictures, the numbers are text) */
.rrw .rv-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* ── Drawn stars ──
   The star is an SVG path, not a font glyph, so the fill is a geometric clip and
   not a rounded-off character. viewBox "-2 0 28 24" pads a 24-wide shape out to a
   28-wide box: five stars are then five equal boxes, and clipping the row at
   average/5 lands exactly on the star it should. */
.rrw .rv-stars{position:relative;display:inline-flex;line-height:0;font-size:22px;
  color:var(--c-border)}
.rrw .rv-star{display:block;height:1em;width:calc(1em * 28 / 24);flex-shrink:0}
.rrw .rv-stars-row{display:inline-flex;white-space:nowrap}
/* The second, clipped copy. Its width is the decimal fill; overflow hides the
   rest of the fifth star. The stars inside keep their natural size because the
   row does not shrink — an over-eager flex child here would silently rescale the
   shape instead of cutting it. */
.rrw .rv-stars-fill{position:absolute;top:0;left:0;bottom:0;overflow:hidden;
  color:var(--c-accent)}
.rrw .rv-stars-fill .rv-star{flex-shrink:0}

/* ── Rating panel ── */
.rrw .rv-panel{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;
  gap:clamp(22px,4vw,52px);padding:clamp(20px,3vw,28px);border:1px solid var(--c-border);
  border-radius:var(--c-radius);background:var(--c-bg);max-width:760px;margin:0 auto}
.rrw .rv-average{display:flex;align-items:center;gap:16px;flex-shrink:0}
.rrw .rv-average-num{font-family:var(--c-heading);font-weight:600;line-height:1;
  color:var(--c-head);font-size:clamp(40px,6vw,58px);font-variant-numeric:tabular-nums}
.rrw .rv-average-side{display:flex;flex-direction:column;gap:8px;align-items:flex-start}
.rrw .rv-scale{font-size:13px;color:var(--c-text);opacity:.7}
.rrw .rv-total{font-size:13px;color:var(--c-text);opacity:.7;text-align:center;
  margin:0 0 14px;font-variant-numeric:tabular-nums}

/* ── Distribution bars ──
   The longest bar is the reference, not the total: against a dominant 5-star
   count the 1-star bar would be half a pixel and the block would read as four
   empty rows. The exact counts are text on every row, so nothing here depends on
   reading a length. */
/* align-content:start, because when the panel stacks the list becomes a column
   flex child and its auto grid rows would stretch to fill the leftover height —
   five 8px bars silently spread out over 260px. */
.rrw .rv-hist{flex:1 1 260px;min-width:220px;max-width:380px;list-style:none;margin:0;
  padding:0;display:grid;gap:7px;align-content:start}
.rrw .rv-hist-row{display:grid;grid-template-columns:auto minmax(0,1fr) auto;
  align-items:center;gap:10px}
.rrw .rv-hist-label{display:inline-flex;align-items:center;gap:4px;font-size:12px;
  color:var(--c-text);opacity:.75;flex-shrink:0;font-variant-numeric:tabular-nums}
.rrw .rv-hist-label .rv-star{height:11px;width:calc(11px * 28 / 24);color:var(--c-accent)}
.rrw .rv-hist-track{display:block;height:8px;border-radius:999px;
  background:var(--c-bg-muted);overflow:hidden}
/* The bar has NO entry animation on purpose. A grow-in would be wrong for its
   first frames — the bar is the count — it plays unseen whenever the block loads
   below the fold, and every headless QA screenshot catches it at scaleX(0), i.e.
   five empty tracks. The only motion in the block is the hover lift below, which
   the reduced-motion query switches off. */
.rrw .rv-hist-bar{display:block;height:100%;border-radius:999px;background:var(--c-accent);
  transition:filter .16s}
.rrw .rv-hist-row:hover .rv-hist-bar{filter:brightness(1.08)}
.rrw .rv-hist-count{font-size:12px;color:var(--c-text);opacity:.75;white-space:nowrap;
  flex-shrink:0;font-variant-numeric:tabular-nums}
@media (prefers-reduced-motion:reduce){.rrw .rv-hist-bar{transition:none}}

.rrw .rv-footnote{font-size:12px;color:var(--c-text);opacity:.6;text-align:center;
  margin:14px 0 0}

/* ── Framed reviews ──
   Two borders with a gap between them: a frame, not a card. */
.rrw .rv-quotes{display:grid;grid-template-columns:repeat(2,1fr);gap:18px;
  margin-top:clamp(24px,3vw,36px);max-width:760px;margin-left:auto;margin-right:auto}
.rrw .rv-frame{margin:0;border:1px solid var(--c-border);border-radius:var(--c-radius);
  padding:7px;background:var(--c-bg)}
.rrw .rv-frame blockquote{margin:0}
.rrw .rv-frame figcaption{margin:0}
.rrw .rv-frame-inner{border:1px solid color-mix(in oklab,var(--c-accent) 30%,transparent);
  border-radius:calc(var(--c-radius) / 2);padding:18px;height:100%}
.rrw .rv-frame .rv-stars{font-size:15px;margin-bottom:10px}
.rrw .rv-frame .quote{margin-bottom:10px}

/* ── Narrow ──
   Under ~52rem the average and five labelled bars do not fit side by side. */
@media (max-width:760px){
  .rrw .rv-panel{flex-direction:column;align-items:stretch}
  .rrw .rv-average{justify-content:center}
  .rrw .rv-hist{flex:0 0 auto;max-width:none}
  .rrw .rv-quotes{grid-template-columns:1fr}
}
.rrw.mobile .rv-panel{flex-direction:column;align-items:stretch;gap:22px}
.rrw.mobile .rv-average{justify-content:center}
.rrw.mobile .rv-average-num{font-size:38px}
.rrw.mobile .rv-stars{font-size:19px}
.rrw.mobile .rv-hist{flex:0 0 auto;max-width:none}
.rrw.mobile .rv-quotes{grid-template-columns:1fr}

/* ── Groups: enquiry-mailto ──
   For shared.css, to be pasted next to the existing .groups-banner rules
   (currently around line 366). Tokens only, no --sp-* (shared.css has none),
   no colour or font literal that is not already a token.

   Every selector is written as its own rule where a newer selector is
   involved (:user-invalid), because a browser that rejects one selector in a
   comma list drops the whole rule with it. */

.rrw .groups-enq{max-width:640px;margin-top:24px;padding:clamp(18px,2.6vw,30px);background:var(--c-bg-muted);border:1px solid var(--c-border);border-radius:var(--c-radius)}
.rrw .groups-enq-h{font-size:clamp(19px,2.1vw,25px);color:var(--c-head);margin-bottom:8px}
.rrw .groups-enq-note{font-size:14px;line-height:1.6;color:var(--c-text);opacity:.72;margin:0 0 8px;max-width:52ch}
.rrw .groups-enq-required{font-size:12.5px;color:var(--c-text);opacity:.6;margin:0 0 18px}
.rrw .groups-enq-req{color:var(--c-accent);margin-left:3px}

/* auto-fit, not a breakpoint: the fields answer to the width of the block, so
   the same rule holds inside the 390px preview frame, in a narrow column on a
   wide page, and on a phone. */
.rrw .groups-enq-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:14px 18px}
.rrw .groups-enq-field{display:flex;flex-direction:column;gap:6px;margin:0;min-width:0}
.rrw .groups-enq-field.wide{grid-column:1/-1}
.rrw .groups-enq-field label{font-size:11px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--c-text);opacity:.78}
.rrw .groups-enq-field input,.rrw .groups-enq-field textarea{width:100%;font-family:var(--c-body);font-size:15px;line-height:1.5;color:var(--c-text);background:var(--c-bg);border:1px solid var(--c-border);border-radius:var(--c-radius);padding:11px 13px;transition:border-color .16s,box-shadow .16s}
.rrw .groups-enq-field textarea{resize:vertical;min-height:96px}
.rrw .groups-enq-field input:focus-visible,.rrw .groups-enq-field textarea:focus-visible{outline:none;border-color:var(--c-accent);box-shadow:0 0 0 3px color-mix(in oklab,var(--c-accent) 22%,transparent)}
/* :user-invalid, not :invalid — an empty required field is invalid from the
   first paint, and a form that is red before it is touched teaches nothing. */
.rrw .groups-enq-field input:user-invalid,.rrw .groups-enq-field textarea:user-invalid{border-color:var(--c-accent)}

.rrw .groups-enq-actions{display:flex;flex-wrap:wrap;align-items:center;gap:14px;margin-top:20px}
/* .cbtn is written for an <a>; a <button> needs the family said out loud. */
.rrw .groups-enq-submit{font-family:var(--c-body)}

.rrw .groups-enq-receipt{margin:14px 0 0;font-size:13.5px;line-height:1.6;color:var(--c-text);max-width:56ch}
.rrw .groups-enq-receipt:empty{display:none}
.rrw .groups-enq-receipt[data-state="sent"]{padding-left:12px;border-left:2px solid var(--c-accent);animation:groups-enq-in .22s ease-out}
@keyframes groups-enq-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){
  .rrw .groups-enq-receipt[data-state="sent"]{animation:none}
  .rrw .groups-enq-field input,.rrw .groups-enq-field textarea{transition:none}
}

.rrw.mobile .groups-enq{padding:18px}
.rrw.mobile .groups-enq-grid{grid-template-columns:1fr}
