/* ==========================================================================
   Roll Wit Rory — stylesheet
   ONE external file, cached once and reused on every page. The old site
   inlined ~1MB of CSS+JS+data into every single page load; this is ~14KB
   fetched once. Same visual language: ember accent, Fraunces display,
   score-tier card fields and 3-Pillar bars carried over from the app build.
   ========================================================================== */

:root{
  --bg:oklch(13% 0.018 292);
  --bg-alt:oklch(15.5% 0.02 292);
  --surface:oklch(20% 0.026 292);
  --surface-hover:oklch(24.5% 0.03 292);
  --border:oklch(30% 0.03 292);
  --text:oklch(95% 0.012 90);
  --text-dim:oklch(76% 0.03 90);
  --text-faint:oklch(55% 0.02 90);
  --ember:oklch(75% 0.16 55);
  --ember-dim:oklch(58% 0.13 50);
  --sage:oklch(75% 0.15 150);
  --haze:oklch(75% 0.15 315);
  --zest:oklch(80% 0.17 108);

  /* Nose / Taste / High. These three were already in the palette, which
     is why the app's scoring language dropped in without new colour. */
  --p-nose:var(--zest);
  --p-taste:var(--sage);
  --p-high:var(--haze);

  --font-display:'Fraunces',Georgia,serif;
  --font-body:'Work Sans',system-ui,-apple-system,sans-serif;
  --font-mono:'IBM Plex Mono',ui-monospace,SFMono-Regular,monospace;
  --radius:14px;
  --max:1120px;
}

*{box-sizing:border-box;margin:0;padding:0;}
html{scroll-behavior:smooth;}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto;}
  *{animation-duration:.01ms !important; transition-duration:.01ms !important;}
}

body{
  background:var(--bg); color:var(--text);
  font-family:var(--font-body); line-height:1.7;
  -webkit-font-smoothing:antialiased;
}
/* Faint measurement grid — the app's HUD backdrop, dialled down to read
   as paper texture. Fixed so it doesn't parallax while scrolling. */
body::before{
  content:''; position:fixed; inset:0; z-index:0; pointer-events:none;
  background-image:
    linear-gradient(to right, oklch(100% 0 0 / .02) 1px, transparent 1px),
    linear-gradient(to bottom, oklch(100% 0 0 / .02) 1px, transparent 1px);
  background-size:44px 44px;
}
main,.site-header,.site-footer{position:relative; z-index:1;}
img{max-width:100%; height:auto; display:block;}
a{color:inherit;}

.wrap{max-width:var(--max); margin:0 auto; padding:0 22px;}
.skip{
  position:absolute; left:-9999px; top:0; background:var(--ember); color:#181207;
  padding:12px 18px; z-index:100; font-family:var(--font-mono); font-size:.8rem;
}
.skip:focus{left:0;}

/* ---------- type ---------- */
.eyebrow{
  font-family:var(--font-mono); font-size:.68rem; letter-spacing:.2em;
  text-transform:uppercase; color:var(--ember); margin-bottom:14px;
}
.eyebrow::before{content:'// '; color:var(--text-faint);}
h1,h2,h3{font-family:var(--font-display); font-weight:600; line-height:1.15;}
.section{padding:78px 0;}
.section-alt{background:var(--bg-alt);}
.section-title{font-size:clamp(1.8rem,5vw,2.5rem); margin-bottom:10px;}
.section-sub{color:var(--text-dim); max-width:60ch; margin-bottom:34px;}

/* ---------- header ---------- */
.site-header{
  position:sticky; top:0; z-index:50;
  background:oklch(13% 0.018 292 / .86); backdrop-filter:blur(10px);
  border-bottom:1px solid var(--border);
}
.site-header .wrap{display:flex; align-items:center; justify-content:space-between; gap:16px; padding-top:12px; padding-bottom:12px;}
/* Block wordmark: wide single line, sized by height so it stays crisp. */
.wordmark img{height:17px; width:auto;}
@media (max-width:400px){ .wordmark img{height:14px;} }
.nav-links{display:flex; gap:22px; list-style:none; flex-wrap:wrap;}
.nav-links a{
  font-family:var(--font-mono); font-size:.78rem; color:var(--text-dim);
  text-decoration:none; transition:color .2s ease;
}
.nav-links a:hover,.nav-links a:focus-visible{color:var(--ember);}
@media (max-width:660px){ .nav-links{gap:14px;} .nav-links a{font-size:.72rem;} }

/* ---------- hero ---------- */
.hero{padding:70px 0 54px; text-align:center;}
.hero-logo{max-width:min(560px,88vw); margin:0 auto 26px;}
.hero h1{font-size:clamp(1.5rem,4.4vw,2.1rem); margin-bottom:14px;}
.hero p{color:var(--text-dim); max-width:56ch; margin:0 auto 26px;}
.hero-actions{display:flex; gap:12px; justify-content:center; flex-wrap:wrap;}
.btn{
  display:inline-block; padding:13px 26px; border-radius:30px; cursor:pointer;
  font-family:var(--font-mono); font-size:.8rem; letter-spacing:.06em;
  text-transform:uppercase; text-decoration:none; border:1px solid var(--border);
  background:transparent; color:var(--text); transition:all .22s ease;
}
.btn:hover{border-color:var(--ember); color:var(--ember);}
.btn-primary{background:var(--ember); border-color:var(--ember); color:#181207; font-weight:600;}
.btn-primary:hover{filter:brightness(1.08); color:#181207;}

/* stats strip */
.stats{display:flex; gap:26px; justify-content:center; flex-wrap:wrap; margin-top:34px;}
.stat b{display:block; font-family:var(--font-display); font-size:1.7rem; font-variant-numeric:tabular-nums;}
.stat span{font-family:var(--font-mono); font-size:.62rem; letter-spacing:.14em; text-transform:uppercase; color:var(--text-faint);}

/* ---------- controls (progressive enhancement: hidden until JS) ---------- */
.controls{display:none; gap:10px; flex-wrap:wrap; margin-bottom:26px;}
.js .controls{display:flex;}
.search-field{
  display:flex; align-items:center; gap:10px; flex:1 1 240px;
  background:var(--surface); border:1px solid var(--border); border-radius:30px; padding:11px 18px;
}
.search-field:focus-within{border-color:var(--ember-dim);}
.search-field input{
  flex:1; min-width:0; background:none; border:none; outline:none;
  color:var(--text); font-family:var(--font-mono); font-size:.85rem;
}
.chip-btn{
  font-family:var(--font-mono); font-size:.7rem; letter-spacing:.06em; text-transform:uppercase;
  padding:9px 15px; border-radius:20px; border:1px solid var(--border);
  background:var(--surface); color:var(--text-dim); cursor:pointer; transition:all .2s ease;
}
.chip-btn:hover{border-color:var(--ember-dim); color:var(--text);}
.chip-btn[aria-pressed="true"]{background:var(--ember); border-color:var(--ember); color:#181207; font-weight:600;}
.result-count{font-family:var(--font-mono); font-size:.7rem; letter-spacing:.1em; text-transform:uppercase; color:var(--text-faint); margin-bottom:16px;}

/* ---------- review cards ---------- */
.grid{display:grid; grid-template-columns:repeat(auto-fill,minmax(266px,1fr)); gap:18px;}
.card{
  position:relative; overflow:hidden; display:block;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius);
  padding:24px; text-decoration:none; color:inherit;
  transition:border-color .25s ease, transform .25s ease;
}
.card:hover,.card:focus-visible{border-color:var(--ember-dim); transform:translateY(-4px);}
/* Score field: tinted by tier, glow placed from a hash of the name so a
   review always looks like itself. Positioned pseudo-element, so content
   needs z-index to sit above it. */
.card::before{
  content:''; position:absolute; inset:0; z-index:0; pointer-events:none;
  opacity:.55; transition:opacity .3s ease;
  background:
    radial-gradient(130% 95% at var(--fx,30%) var(--fy,14%), var(--tint,transparent) 0%, transparent 64%),
    radial-gradient(95% 75% at calc(100% - var(--fx,30%)) 88%, var(--tint2,transparent) 0%, transparent 62%);
}
.card:hover::before{opacity:.9;}
.card > *{position:relative; z-index:1;}

.tier-elite{--tint:oklch(75% 0.15 315 / .34); --tint2:oklch(75% 0.16 55 / .16);}
.tier-high {--tint:oklch(75% 0.16 55 / .30);  --tint2:oklch(80% 0.17 108 / .13);}
.tier-mid  {--tint:oklch(75% 0.15 150 / .17); --tint2:oklch(75% 0.16 55 / .09);}
.tier-low  {--tint:oklch(55% 0.02 90 / .18);  --tint2:transparent;}

.card-top{display:flex; align-items:center; justify-content:space-between; gap:10px; margin-bottom:14px;}
.tag{
  font-family:var(--font-mono); font-size:.6rem; letter-spacing:.1em; text-transform:uppercase;
  color:var(--sage); border:1px solid var(--border); padding:4px 9px; border-radius:20px;
}
.score{font-family:var(--font-display); font-size:1.5rem; font-weight:600; font-variant-numeric:tabular-nums;}
.score-elite{color:var(--haze);} .score-high{color:var(--ember);}
.score-mid{color:var(--text);}   .score-low{color:var(--text-faint);}
.card h3{font-size:1.12rem; margin-bottom:8px;}
.brand{
  display:inline-block; font-family:var(--font-mono); font-size:.68rem;
  color:var(--ember); border:1px solid var(--border); padding:3px 10px; border-radius:20px;
}
.blurb{color:var(--text-dim); font-size:.86rem; line-height:1.6; margin-top:12px;}
.card-meta{font-family:var(--font-mono); font-size:.64rem; color:var(--text-faint); margin-top:12px;}
.effects{display:flex; gap:5px; flex-wrap:wrap; margin-top:12px;}
.effect{
  font-family:var(--font-mono); font-size:.58rem; letter-spacing:.08em; text-transform:uppercase;
  color:var(--text-dim); border:1px solid var(--border); padding:3px 8px; border-radius:12px;
}
.pick{background:var(--ember); color:#181207; border-color:var(--ember); font-weight:600;}

/* 3-Pillar bars */
.pillars{display:flex; gap:5px; margin:14px 0 2px; width:min(190px,100%);}
.pillar{position:relative; flex:1; height:3px; border-radius:2px; background:oklch(100% 0 0 / .10); overflow:hidden;}
.pillar::after{
  content:''; position:absolute; inset:0 auto 0 0; width:var(--v,0%);
  border-radius:inherit; background:var(--pc,var(--ember));
}
.p-nose{--pc:var(--p-nose);} .p-taste{--pc:var(--p-taste);} .p-high{--pc:var(--p-high);}

/* ---------- review page ---------- */
.review-head{padding:52px 0 30px;}
.back{
  display:inline-block; margin-bottom:26px; color:var(--text-dim); text-decoration:none;
  font-family:var(--font-mono); font-size:.78rem;
}
.back:hover{color:var(--ember);}
.review-title{font-size:clamp(2rem,6.4vw,3rem); margin:10px 0 12px;}
.review-score{display:flex; align-items:baseline; gap:12px; flex-wrap:wrap; margin:20px 0 8px;}
.review-score .big{font-family:var(--font-display); font-size:clamp(3rem,11vw,4.6rem); font-weight:600; line-height:1; font-variant-numeric:tabular-nums;}
.review-score .out{font-family:var(--font-mono); font-size:.8rem; color:var(--text-faint);}

.pillar-block{margin-bottom:34px;}
.pillar-head{display:flex; align-items:baseline; justify-content:space-between; gap:12px; margin-bottom:9px;}
.pillar-name{font-family:var(--font-display); font-size:1.35rem; font-weight:600;}
.pillar-val{font-family:var(--font-mono); font-size:1rem; font-variant-numeric:tabular-nums;}
.pillar-track{height:4px; border-radius:2px; background:oklch(100% 0 0 / .10); overflow:hidden; margin-bottom:13px;}
.pillar-fill{height:100%; border-radius:inherit;}
.pillar-block p{color:var(--text-dim);}

.datagrid{display:grid; grid-template-columns:repeat(auto-fit,minmax(168px,1fr)); gap:12px; margin:12px 0 0; list-style:none;}
.datagrid li{background:var(--surface); border:1px solid var(--border); border-radius:10px; padding:12px 14px;}
.datagrid dt{font-family:var(--font-mono); font-size:.6rem; letter-spacing:.12em; text-transform:uppercase; color:var(--text-faint); margin-bottom:4px;}
.datagrid dd{font-size:.9rem;}
.yes{color:var(--sage);} .no{color:var(--ember);}

.pullquote{
  border-left:3px solid var(--ember); padding-left:18px; margin:34px 0;
  font-family:var(--font-display); font-size:1.28rem; line-height:1.5;
}
.legacy{margin-top:18px;}
.legacy-row{display:flex; gap:16px; align-items:baseline; padding:14px 0; border-bottom:1px solid var(--border);}
.legacy-row b{font-family:var(--font-display); font-size:1.4rem; min-width:56px; font-variant-numeric:tabular-nums;}
.legacy-row span{font-family:var(--font-mono); font-size:.66rem; letter-spacing:.12em; text-transform:uppercase; color:var(--text-dim); display:block; margin-bottom:3px;}
.legacy-row p{color:var(--text-dim); font-size:.9rem;}

/* ---------- topics ---------- */
.topic-group{margin-bottom:12px; border-bottom:1px solid var(--border);}
.topic-group summary{
  cursor:pointer; padding:16px 0; display:flex; align-items:center; gap:10px;
  font-family:var(--font-mono); font-size:.8rem; letter-spacing:.08em; text-transform:uppercase;
  color:var(--text-dim); list-style:none;
}
.topic-group summary::-webkit-details-marker{display:none;}
.topic-group summary::before{content:'+'; color:var(--ember); font-size:1.1rem; transition:transform .2s ease;}
.topic-group[open] summary::before{content:'−';}
.topic-group summary:hover{color:var(--text);}
.topic-count{margin-left:auto; font-size:.66rem; color:var(--text-faint); border:1px solid var(--border); border-radius:20px; padding:2px 9px;}
.topic{padding:0 0 22px;}
.topic h3{font-size:1.05rem; margin-bottom:6px;}
.topic p{color:var(--text-dim); font-size:.92rem;}
.topic .tag{margin-bottom:8px; display:inline-block; color:var(--haze);}

/* ---------- footer ---------- */
.site-footer{border-top:1px solid var(--border); padding:44px 0; margin-top:40px;}
.site-footer .wrap{display:flex; gap:20px; justify-content:space-between; flex-wrap:wrap; align-items:center;}
.site-footer a{color:var(--ember); text-decoration:none; font-family:var(--font-mono); font-size:.78rem;}
.site-footer a:hover{text-decoration:underline;}
.site-footer p{font-family:var(--font-mono); font-size:.7rem; color:var(--text-faint);}
.footer-links{display:flex; gap:18px; flex-wrap:wrap;}

.empty{color:var(--text-faint); font-family:var(--font-mono); font-size:.85rem; padding:30px 0;}
[hidden]{display:none !important;}

/* ---------- 3-Pillar triangle (the signature visual) ----------
   Server-rendered SVG, so it's in the markup and needs no JavaScript. */
.tri{display:block; overflow:visible;}
.card-figure{display:flex; align-items:center; gap:14px; margin:12px 0 2px;}
.card-figure .tri{flex:0 0 auto; opacity:.92;}
/* Numbers beside the triangle instead of a second set of bars — the
   triangle already shows the shape, so the readout adds the values
   rather than repeating the picture. */
.card-figure-read{display:flex; flex-direction:column; gap:5px;}
.card-figure-read span{
  display:flex; align-items:center; gap:7px;
  font-family:var(--font-mono); font-size:.63rem; letter-spacing:.06em;
  text-transform:uppercase; color:var(--text-faint);
}
.card-figure-read i{width:7px; height:7px; border-radius:2px; flex:0 0 auto;}
.card-figure-read b{color:var(--text); font-weight:500; font-variant-numeric:tabular-nums;}
.card:hover .tri{opacity:1;}

.review-hero{
  display:flex; align-items:center; justify-content:space-between;
  gap:24px; flex-wrap:wrap; margin:22px 0 10px;
}
.index-note{
  font-family:var(--font-mono); font-size:.68rem; letter-spacing:.14em;
  text-transform:uppercase; color:var(--text-faint); margin-top:2px;
}
@media (max-width:560px){
  .review-hero{gap:14px;}
  .review-hero .tri{width:170px; height:170px; margin:0 auto;}
}

/* ---------- community ---------- */
.byline{font-family:var(--font-mono); font-size:.64rem; letter-spacing:.05em; color:var(--text-faint); margin-top:10px;}
.byline-big{font-size:.8rem; color:var(--text-dim); margin:14px 0 4px; letter-spacing:0;}
.byline-big a{color:var(--ember); text-decoration:none;}
.byline-big a:hover{text-decoration:underline;}
.card-community{border-style:dashed;}
.creator-strip{display:flex; gap:12px; flex-wrap:wrap; margin:0 0 30px;}
.creator-chip{
  display:flex; flex-direction:column; gap:2px; text-decoration:none;
  background:var(--surface); border:1px solid var(--border); border-radius:12px;
  padding:14px 20px; transition:border-color .2s ease;
}
.creator-chip:hover{border-color:var(--ember-dim);}
.creator-chip b{font-family:var(--font-display); font-size:1.05rem;}
.creator-chip span{font-family:var(--font-mono); font-size:.62rem; letter-spacing:.1em; text-transform:uppercase; color:var(--text-faint);}

/* ---------- charts ---------- */
.chart{margin:18px 0 8px;}
.chart svg{width:100%; height:auto; display:block; overflow:visible;}
.chart figcaption{
  font-family:var(--font-mono); font-size:.66rem; letter-spacing:.04em;
  color:var(--text-faint); margin-top:10px;
}

/* ---------- discussion thread (see assets/comments.js) ----------
   The whole section ships with [hidden] on it and is revealed only once
   comments.php answers. Styling stays deliberately quiet: a review page
   should read as a review with a conversation under it, not as a comment
   box with an article stapled above. */
.comments{padding-top:0; max-width:760px;}
.comment-count{
  font-family:var(--font-mono); font-size:.7rem; color:var(--text-faint);
  letter-spacing:.1em;
}
.comment-count:not(:empty)::before{content:'('; }
.comment-count:not(:empty)::after{content:')'; }
.comment-status{color:var(--text-faint); font-size:.92rem; margin:14px 0;}

.comment-list{list-style:none; margin:18px 0 0;}
.comment{
  padding:16px 0; border-top:1px solid var(--border);
}
.comment:first-child{border-top:none;}
.comment-head{
  display:flex; align-items:baseline; gap:10px; flex-wrap:wrap; margin-bottom:5px;
}
.comment-name{
  font-family:var(--font-mono); font-size:.78rem; letter-spacing:.04em;
  color:var(--text-dim);
}
.comment-owner .comment-name{color:var(--ember);}
/* Only ever applied to comments the backend flagged is_owner, which it
   does solely for posts carrying the admin key — never from a typed name. */
.comment-badge{
  font-family:var(--font-mono); font-size:.56rem; letter-spacing:.14em;
  text-transform:uppercase; color:var(--ember);
  border:1px solid var(--ember-dim); border-radius:20px; padding:2px 8px;
}
.comment-time{font-family:var(--font-mono); font-size:.66rem; color:var(--text-faint);}
.comment-body{color:var(--text); white-space:pre-wrap; overflow-wrap:anywhere;}

/* composer — one line until clicked */
.comment-compose{margin-top:26px;}
.comment-open{
  font-family:var(--font-mono); font-size:.72rem; letter-spacing:.1em;
  text-transform:uppercase; color:var(--text-faint); background:none;
  border:none; border-bottom:1px dashed var(--border); padding:6px 0;
  cursor:pointer; transition:color .2s ease, border-color .2s ease;
}
.comment-open:hover{color:var(--ember); border-color:var(--ember-dim);}
.comments-writing .comment-open{display:none;}
.comments-writing .comment-form{display:block;}
.comment-form{display:none;}

.comment-form textarea,
.comment-form input[type=text]{
  width:100%; background:var(--surface); color:var(--text);
  border:1px solid var(--border); border-radius:var(--radius);
  padding:12px 14px; font-family:var(--font-body); font-size:.95rem;
  line-height:1.6; resize:vertical;
}
.comment-form textarea:focus,
.comment-form input[type=text]:focus{outline:none; border-color:var(--ember-dim);}
.comment-form input[type=text]:disabled{opacity:.45;}
.comment-form-row{
  display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin-top:10px;
}
.comment-form-row input[type=text]{flex:1 1 180px; width:auto;}
.comment-anon{
  display:flex; align-items:center; gap:7px; cursor:pointer;
  font-family:var(--font-mono); font-size:.68rem; letter-spacing:.08em;
  text-transform:uppercase; color:var(--text-faint); white-space:nowrap;
}
.comment-anon input{accent-color:var(--ember); width:15px; height:15px;}
.comment-form .btn{padding:11px 24px;}
.comment-error{
  margin-top:10px; color:var(--ember); font-size:.85rem;
}
@media (max-width:520px){
  .comment-form-row .btn{flex:1 1 100%; text-align:center;}
}

/* ---------- community entries ----------
   97 reviews on one page. Collapsed rows, not cards: a grid of 97 cards
   is a scroll marathon, a list of 97 rows is scannable. Everything is in
   the markup regardless of open state, so search engines and ctrl-F see
   all of it. */
.c-entries{margin-top:8px; border-top:1px solid var(--border);}
.c-entry{border-bottom:1px solid var(--border);}
.c-entry > summary{
  display:flex; align-items:center; gap:14px; padding:13px 4px;
  cursor:pointer; list-style:none; transition:background .16s ease;
}
.c-entry > summary::-webkit-details-marker{display:none;}
.c-entry > summary:hover{background:var(--surface);}
.c-entry[open] > summary{background:var(--surface);}
.c-entry-score{
  font-family:var(--font-display); font-size:1.15rem; font-variant-numeric:tabular-nums;
  min-width:2.9em; text-align:right; flex:none;
}
.c-entry-name{flex:1 1 auto; min-width:0; font-weight:500;}
.c-entry-brand{color:var(--text-faint); font-weight:400;}
.c-entry-cat{
  font-family:var(--font-mono); font-size:.62rem; letter-spacing:.1em;
  text-transform:uppercase; color:var(--text-faint); flex:none;
}
.c-entry-body{padding:4px 4px 22px; max-width:70ch;}
/* mrkurtin's four numeric criteria, as a compact score row. */
.c-entry-scores{
  display:flex; flex-wrap:wrap; gap:10px; margin-bottom:16px;
}
.c-score{
  background:var(--surface); border-radius:var(--radius);
  padding:9px 14px; text-align:center; min-width:82px;
}
.c-score-val{
  display:block; font-family:var(--font-display); font-size:1.25rem;
  font-variant-numeric:tabular-nums; line-height:1.2;
}
.c-score-label{
  display:block; font-family:var(--font-mono); font-size:.58rem;
  letter-spacing:.12em; text-transform:uppercase; color:var(--text-faint);
  margin-top:3px;
}
/* A named criterion the author wrote out instead of scoring —
   mrkurtin's "Dry Hit", and the separate Taste field. */
.c-entry-written{color:var(--text-dim); margin-bottom:12px; line-height:1.65;}
.c-entry-written b{
  font-family:var(--font-mono); font-size:.62rem; letter-spacing:.12em;
  text-transform:uppercase; color:var(--text-faint); margin-right:8px;
}
/* LOAD-BEARING. SquirtleRollz's 57 reviews are whole posts with her own
   line breaks, section headings and @-mentions on their own lines. Drop
   pre-line and all 57 collapse into run-on paragraphs — which is what
   the legacy site avoided with exactly this rule. */
.c-entry-note{
  color:var(--text); margin-bottom:14px;
  white-space:pre-line; overflow-wrap:anywhere; line-height:1.7;
}
.c-entry-grid{margin-top:6px;}
/* An anchored review must land open, or following a link to one lands on
   a collapsed row that looks like nothing happened. */
.c-entry:target{scroll-margin-top:90px;}
.c-entry:target > summary{background:var(--surface-hover);}
@media (max-width:560px){
  .c-entry-cat{display:none;}
  .c-entry-score{font-size:1rem; min-width:2.5em;}
}

/* ---------- pillar bars (partially-scored reviews) ----------
   Shown instead of the triangle when nose, taste or high wasn't scored
   — a vape with no nose, an older review with no high. The triangle
   would put the missing vertex at the centre and collapse into a
   sliver, which reads as a savage score rather than "not measured". */
.pillar-figure{flex:none; width:150px; display:flex; flex-direction:column; gap:7px;}
.pfig-row{display:flex; align-items:center; gap:8px;}
.pfig-label{
  font-family:var(--font-mono); font-size:.56rem; letter-spacing:.1em;
  text-transform:uppercase; color:var(--text-faint); width:3.1em; flex:none;
}
.pfig-track{
  flex:1; height:5px; border-radius:3px; background:var(--border); overflow:hidden;
}
.pfig-fill{display:block; height:100%; border-radius:3px;}
.pfig-val{
  font-family:var(--font-display); font-size:.86rem; font-variant-numeric:tabular-nums;
  width:2.4em; text-align:right; flex:none;
}
.pfig-note{
  font-family:var(--font-mono); font-size:.54rem; letter-spacing:.06em;
  color:var(--text-faint); margin-top:2px; line-height:1.4;
}
/* Big version on a review page, where it stands in for the 210px triangle. */
.pillar-figure-big{width:100%; max-width:340px; gap:12px;}
.pillar-figure-big .pfig-label{font-size:.66rem; width:3.4em;}
.pillar-figure-big .pfig-track{height:8px; border-radius:4px;}
.pillar-figure-big .pfig-val{font-size:1.3rem; width:2.8em;}
.pillar-figure-big .pfig-note{font-size:.62rem; margin-top:6px;}

/* ==========================================================================
   MOTION
   Restrained on purpose: nothing loops, nothing moves on its own, nothing
   delays reading. Every effect is a one-shot response to arriving on the
   page or scrolling something into view.

   THE SAFETY RULE, which matters more than the effects:
   no element is ever left at opacity:0 in a static rule. The hidden
   starting state is applied only under .js (set by site.js), so if
   JavaScript fails to load, every one of these elements renders plainly
   visible instead of the page appearing blank. A fade-in that fails
   closed is a broken site.
   ========================================================================== */

@keyframes rwr-fade-up{ from{opacity:0; transform:translateY(8px);} to{opacity:1; transform:none;} }
@keyframes rwr-fade{ from{opacity:0;} to{opacity:1;} }
@keyframes rwr-shimmer{ from{background-position:-320px 0;} to{background-position:320px 0;} }

/* --- reveal on scroll --- */
.js .reveal{opacity:0;}
.js .reveal.in{animation:rwr-fade-up .32s cubic-bezier(.22,.9,.3,1) both;}
/* Cards in a grid come in with a small stagger, capped so a 154-card
   page never waits on a queue. --i is set by site.js, max 8. */
.js .reveal.in{animation-delay:calc(var(--i, 0) * 28ms);}

/* --- the triangle draws itself, once --- */
.js .tri .tri-plot{transform-box:fill-box; transform-origin:center; opacity:0;}
.js .tri.in .tri-plot{animation:rwr-tri .38s cubic-bezier(.2,.9,.3,1) both;}
@keyframes rwr-tri{ from{opacity:0; transform:scale(.35);} to{opacity:1; transform:scale(1);} }

/* --- bars fill from empty --- */
.js .pfig-fill,.js .pillar-fill{transform-origin:left center;}
.js .in .pfig-fill,.js .in .pillar-fill{animation:rwr-bar .45s cubic-bezier(.2,.9,.3,1) both;}
@keyframes rwr-bar{ from{transform:scaleX(0);} to{transform:scaleX(1);} }

/* --- comments fade in as they arrive --- */
.comment{animation:rwr-fade-up .28s ease both;}

/* --- loading skeleton, so something is clearly happening --- */
.comment-skeleton{list-style:none; margin:18px 0 0;}
.comment-skeleton li{
  height:54px; border-radius:var(--radius); margin-bottom:10px;
  background:var(--surface);
  background-image:linear-gradient(90deg, transparent, oklch(100% 0 0 / .045), transparent);
  background-size:320px 100%; background-repeat:no-repeat;
  animation:rwr-shimmer 1.1s linear infinite;
}
.comment-skeleton li:nth-child(2){width:88%;}
.comment-skeleton li:nth-child(3){width:72%;}

/* Motion is decoration. Anyone who's asked their OS to stop animations
   gets the finished state instantly — never a blank page. */
@media (prefers-reduced-motion:reduce){
  .js .reveal,.js .tri .tri-plot{opacity:1 !important;}
  .js .reveal.in,.js .tri.in .tri-plot,.comment,
  .js .in .pfig-fill,.js .in .pillar-fill{animation:none !important; transform:none !important;}
  .comment-skeleton li{animation:none;}
}

/* An unscored pillar on a review page: notes kept, number and bar gone. */
.pillar-block-unscored{opacity:.72;}
.pillar-na{
  font-family:var(--font-mono); font-size:.62rem; letter-spacing:.12em;
  text-transform:uppercase; color:var(--text-faint);
}

/* ==========================================================================
   ACHIEVEMENTS + PLAYER
   ========================================================================== */

/* Styled to be indistinguishable from the real links beside it —
   an underlined button in a row of plain links reads as broken. */
.footer-link-btn{
  background:none; border:none; padding:0; margin:0; cursor:pointer;
  text-decoration:none;
  /* copied from `.site-footer a` deliberately — inheriting the body font
     here is exactly what made it look like a stray bit of UI */
  color:var(--ember); font-family:var(--font-mono); font-size:.78rem;
}
.footer-link-btn:hover{text-decoration:underline;}
.footer-link-btn:hover{color:var(--ember);}
.footer-link-btn .ach-tally{
  font-family:var(--font-mono); font-size:.72em; color:var(--text-faint);
  margin-left:6px;
}

/* --- unlock toast --- */
.ach-toast{
  position:fixed; left:50%; bottom:calc(22px + env(safe-area-inset-bottom,0px));
  transform:translate(-50%, 16px); opacity:0; z-index:120;
  display:flex; align-items:center; gap:13px;
  background:var(--surface); border:1px solid var(--ember-dim);
  border-radius:var(--radius); padding:12px 18px; max-width:min(92vw,380px);
  box-shadow:0 10px 40px rgba(0,0,0,.5);
  transition:opacity .3s ease, transform .3s cubic-bezier(.2,.9,.3,1);
}
.ach-toast.in{opacity:1; transform:translate(-50%, 0);}
.ach-toast-icon{font-size:1.5rem; line-height:1; flex:none;}
.ach-toast b{display:block; font-family:var(--font-display); font-size:.98rem;}
.ach-toast small{display:block; color:var(--text-dim); font-size:.78rem; line-height:1.45;}

/* --- panel --- */
.ach-dialog{
  background:var(--bg-alt); color:var(--text); border:1px solid var(--border);
  border-radius:16px; padding:26px; max-width:520px; width:92vw;
}
.ach-dialog::backdrop{background:rgba(0,0,0,.72);}
.ach-head{margin-bottom:18px;}
.ach-head h2{font-family:var(--font-display); font-size:1.35rem;}
.ach-rank{
  font-family:var(--font-mono); font-size:.7rem; letter-spacing:.12em;
  text-transform:uppercase; color:var(--ember); margin-top:4px;
}
.ach-list{list-style:none; max-height:56vh; overflow:auto; margin-bottom:18px;}
.ach-row{
  display:flex; align-items:flex-start; gap:13px; padding:11px 0;
  border-top:1px solid var(--border);
}
.ach-row:first-child{border-top:none;}
.ach-row.locked{opacity:.45;}
.ach-icon{font-size:1.2rem; width:26px; text-align:center; flex:none; line-height:1.4;}
.ach-row b{display:block; font-size:.94rem;}
.ach-row small{display:block; color:var(--text-dim); font-size:.79rem; line-height:1.45;}

/* --- session mix player --- */
.player{
  position:fixed; left:0; right:0; bottom:0; z-index:90;
  background:oklch(15.5% 0.02 292 / .93); backdrop-filter:blur(12px);
  border-top:1px solid var(--border);
  padding-bottom:env(safe-area-inset-bottom,0px);
}
.player-inner{display:flex; align-items:center; gap:14px; padding:10px 24px;}
.player-btn,.player-skip,.player-close{
  background:none; border:1px solid var(--border); border-radius:50%;
  color:var(--text); cursor:pointer; flex:none;
  width:34px; height:34px; display:flex; align-items:center; justify-content:center;
  font-size:.8rem; transition:border-color .2s ease, color .2s ease;
}
.player-btn:hover,.player-skip:hover,.player-close:hover{border-color:var(--ember); color:var(--ember);}
.player.playing .player-btn{border-color:var(--ember); color:var(--ember);}
.player-meta{flex:1; min-width:0;}
.player-label{
  display:block; font-family:var(--font-mono); font-size:.56rem;
  letter-spacing:.14em; text-transform:uppercase; color:var(--text-faint);
}
.player-track{
  display:block; font-size:.86rem; white-space:nowrap;
  overflow:hidden; text-overflow:ellipsis;
}
.player-vol{width:96px; flex:none; accent-color:var(--ember);}
.player-close{margin-left:2px;}
/* The bar sits over the page bottom, so give the footer room to clear it. */
.site-footer{padding-bottom:84px;}
@media (max-width:560px){
  .player-inner{gap:10px; padding:9px 16px;}
  .player-vol{display:none;}
}

/* ==========================================================================
   FIRST-VISIT TOUR
   Full-bleed overlay. The real homepage is rendered underneath the whole
   time, so if tour.js fails or is blocked the visitor is simply on the
   site — never stranded on a black screen.
   ========================================================================== */
html.tour-open{overflow:hidden;}
.tour{
  position:fixed; inset:0; z-index:200; opacity:0;
  background:radial-gradient(120% 90% at 50% 12%, oklch(20% 0.03 292) 0%, oklch(11% 0.015 292) 60%);
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  padding:32px 26px calc(40px + env(safe-area-inset-bottom,0px));
  text-align:center; cursor:pointer; transition:opacity .42s ease;
}
.tour.in{opacity:1;}
.tour.out{opacity:0; pointer-events:none;}

.tour-progress{
  position:absolute; top:calc(20px + env(safe-area-inset-top,0px)); left:26px; right:26px;
  display:flex; gap:5px;
}
.tour-progress span{
  flex:1; height:2px; background:var(--border); border-radius:2px; overflow:hidden;
  position:relative;
}
.tour-progress span.done{background:var(--ember-dim);}
.tour-progress span.now::after{
  content:''; position:absolute; inset:0; background:var(--ember);
  transform-origin:left; animation:tour-fill 3.4s linear both;
}
@keyframes tour-fill{ from{transform:scaleX(0);} to{transform:scaleX(1);} }

.tour-stage{max-width:720px; width:100%;}
.tour-scene{animation:tour-in .5s cubic-bezier(.22,.9,.3,1) both;}
@keyframes tour-in{ from{opacity:0; transform:translateY(14px);} to{opacity:1; transform:none;} }

.tour-kicker{
  font-family:var(--font-mono); font-size:.68rem; letter-spacing:.2em;
  text-transform:uppercase; color:var(--ember); margin-bottom:16px;
}
.tour-line{
  font-family:var(--font-display); font-weight:600; line-height:1.12;
  font-size:clamp(2rem, 7vw, 3.6rem); letter-spacing:-.01em;
}
.tour-sub{
  color:var(--text-dim); font-size:clamp(.95rem, 2.4vw, 1.1rem);
  line-height:1.6; max-width:46ch; margin:18px auto 0;
}
.tour-go{margin-top:30px;}

.tour-art{margin:30px auto 0; display:flex; justify-content:center;}
.tour-tri{animation:tour-tri 1s cubic-bezier(.2,.9,.3,1) both; transform-box:fill-box; transform-origin:center;}
@keyframes tour-tri{ from{opacity:0; transform:scale(.3);} to{opacity:1; transform:scale(1);} }
.tour-bars{flex-direction:column; gap:10px; width:min(320px,80vw);}
.tour-bars span{display:block; height:7px; background:var(--surface); border-radius:4px; overflow:hidden;}
.tour-bars i{
  display:block; height:100%; background:var(--ember); border-radius:4px;
  transform-origin:left; animation:tour-bar .8s cubic-bezier(.2,.9,.3,1) both;
}
.tour-bars span:nth-child(1) i{background:var(--text-faint);}
@keyframes tour-bar{ from{transform:scaleX(0);} to{transform:scaleX(1);} }

/* Visible from the first frame. An intro you can't leave is a trap. */
.tour-skip{
  position:absolute; bottom:calc(28px + env(safe-area-inset-bottom,0px)); left:50%;
  transform:translateX(-50%);
  background:none; border:1px solid var(--border); border-radius:30px;
  color:var(--text-faint); cursor:pointer; padding:9px 22px;
  font-family:var(--font-mono); font-size:.7rem; letter-spacing:.12em;
  text-transform:uppercase; transition:color .2s ease, border-color .2s ease;
}
.tour-skip:hover{color:var(--ember); border-color:var(--ember-dim);}

/* ---------- credentials page ---------- */
.cred-block{border-top:1px solid var(--border); padding-top:6px;}
.cred-block:first-of-type{border-top:none;}
.cred-h{font-family:var(--font-display); font-size:1.5rem; margin-bottom:10px;}
.cred-lede{color:var(--text-dim); margin-bottom:14px;}
.cred-block p{color:var(--text-dim); line-height:1.75;}
.cred-note{
  font-family:var(--font-mono); font-size:.7rem; letter-spacing:.06em;
  color:var(--text-faint); margin-top:16px;
}
.cred-pillars{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(200px,1fr));
  gap:14px; margin:20px 0;
}
.cred-pillar{background:var(--surface); border-radius:var(--radius); padding:16px 18px;}
.cred-pillar b{
  display:block; font-family:var(--font-mono); font-size:.66rem;
  letter-spacing:.14em; text-transform:uppercase; margin-bottom:7px;
}
.cred-pillar p{font-size:.88rem; line-height:1.6;}

/* Manual-copy fallback when the Clipboard API is blocked. */
.share-fallback{
  display:block; width:100%; max-width:420px; margin-top:10px;
  background:var(--surface); border:1px solid var(--ember-dim);
  border-radius:var(--radius); padding:10px 13px; color:var(--text);
  font-family:var(--font-mono); font-size:.78rem;
}

/* ---------- update notice ---------- */
.update-bar{
  position:fixed; left:50%; bottom:calc(90px + env(safe-area-inset-bottom,0px));
  transform:translate(-50%,14px); opacity:0; z-index:130;
  display:flex; align-items:center; gap:14px;
  background:var(--surface); border:1px solid var(--ember-dim);
  border-radius:30px; padding:10px 12px 10px 20px;
  max-width:min(92vw,440px); font-size:.86rem;
  box-shadow:0 10px 40px rgba(0,0,0,.5);
  transition:opacity .3s ease, transform .3s cubic-bezier(.2,.9,.3,1);
}
.update-bar.in{opacity:1; transform:translate(-50%,0);}
.update-bar span{flex:1; color:var(--text-dim); line-height:1.4;}
.update-go{
  flex:none; background:var(--ember); border:none; border-radius:30px;
  color:#181207; font-family:var(--font-mono); font-size:.7rem;
  letter-spacing:.1em; text-transform:uppercase; font-weight:600;
  padding:8px 16px; cursor:pointer;
}
.update-go:hover{filter:brightness(1.08);}
.update-dismiss{
  flex:none; background:none; border:none; color:var(--text-faint);
  font-size:1.2rem; line-height:1; cursor:pointer; padding:0 4px;
}
.update-dismiss:hover{color:var(--text);}
.build-stamp{
  font-family:var(--font-mono); font-size:.62rem; color:var(--text-faint);
  margin-left:10px; opacity:.75;
}
@media (max-width:560px){
  .update-bar{left:12px; right:12px; transform:translateY(14px); max-width:none;}
  .update-bar.in{transform:translateY(0);}
}
