/* ===========================================================================
   Portfolio template
   styles.css  —  shared stylesheet for the PHP site (Classic look)
   ---------------------------------------------------------------------------
   Re-skin the whole site from one place: edit the CSS variables in :root
   (light theme) and html[data-theme="dark"] (dark theme) below.
   The Settings panel (owner-only) writes overrides into these same variables.
   =========================================================================== */

:root {
  /* ---- Palette (light) ---- */
  --bg:#FAFAFC; --surface:#FFFFFF; --ink:#16161F; --ink-2:#363744; --soft:#62636F; --faint:#70717F;
  --line:#E7E7EC;
  /* The bracket connectors in Visual style. --line is tuned for box borders
     and is too faint for a 1px rule floating in whitespace. Mixed TOWARD
     --faint rather than hard-coded, so it gains contrast in the dark skins
     too — there --line is dark and --faint is light, and the mix still
     moves away from the background either way. */
  --conn-line:color-mix(in srgb, var(--line) 45%, var(--faint)); --hair:#F0F0F4;
  --accent:#4039D9; --accent-2:#332DBA; --accent-soft:#ECEBFB; --accent-ink:#211C7A;
  /* Text sitting ON the --accent FILL (the A/D/D/I/E chips, the active
     category tab). This was hardcoded #fff in 20 rules, which works on a
     light theme's deep accent and fails on every dark theme's light one —
     white on Night Class's gold measured 2.10. Each theme names its own. */
  --on-accent:#FFFFFF;   /* 7.49:1 */
  /* --accent used AS TEXT. Split from --accent because that token is also a
     BACKGROUND in 25 rules, and the two pull in opposite directions: darkening
     it to make link text readable on a light page simultaneously darkens the
     fill under Bold's near-black label text. Defaults to --accent, so a skin
     only declares this when its accent fails as text. */
  --accent-text:var(--accent);
  --ok:#1F8A5B; --warn:#C2541F;

  --maxw:1180px;
  --radius:16px; --radius-sm:10px;
  /* Standard image aspect ratios — referenced everywhere images render, so the
     whole site stays on a small, consistent set instead of ad-hoc values. */
  --ar-square:1/1; --ar-video:16/9;
  --f-display:"Space Grotesk", system-ui, sans-serif;
  --f-sans:"Hanken Grotesk", system-ui, sans-serif;
  --f-mono:ui-monospace, "SF Mono", Menlo, monospace;

  /* CTA (Contact button) defaults to the accent; the Visual Style editor can
     give it its own color. Typography knobs scale the whole type system. */
  /* --cta-ink follows --on-accent because --cta IS --accent by default: the ink
     that reads on the accent fill is the ink that reads on the button. It was a
     hardcoded #fff, which meant classic's dark theme put white on its
     deliberately-light dark-mode accent — 3.71, and the buttons are 14-15px at
     weight 600, so no large-text exemption applies. A skin that points --cta
     somewhere other than --accent (Syllabus does) still names its own. */
  --cta:var(--accent); --cta-2:var(--accent-2); --cta-ink:var(--on-accent);
  --f-scale:1; --f-head-weight:600; --f-head-tracking:-0.02em;

  /* Ink for the uppercase micro-labels on a resource detail page — "SKILLS"
     (.ds-label), "TRAINING TIME" (.spec-label), "FOR THE FACILITATOR"
     (.frp-group). They share one token on purpose: they are the same 10.56px
     /600 treatment stacked down the same column, so a value that moves one
     has to move all three or the parts list stops matching the Skills label
     directly above it.

     Why not --faint, which they used to take: at 10.56px/600 WCAG counts these
     as NORMAL text (large-text 3:1 needs 24px, or 18.66px at weight 700), so
     they owe 4.5:1 — and --faint measured 2.65:1 on classic, 2.18:1 on warm,
     3.22:1 on syllabus. --soft clears 4.5:1 on every skin and both themes; the
     only places it doesn't are saturated section bands, which the two band
     skins override for themselves (see skin-bold.css / skin-warm.css).

     Derived from --soft rather than a fixed hex so it inherits per-skin and
     per-theme, and so it rides the owner's "Muted text" role in the Visual
     Style editor — --faint is wired to no control at all, so a label that
     failed there could not be corrected by the owner. */
  --label-ink:var(--soft);

  /* Sticky-stack offsets. Three layers pin on top of each other on a resource
     page — .top (top:0), .addie-nav, then .detail-section .section-head — and
     each one's offset is just the measured height of everything above it. That
     arithmetic was inlined at three call sites, so a skin that made the top bar
     taller broke it silently: filmstrip's 10px sprocket strip plus its 3px rule
     put the bar at 83px while the ADDIE nav went on pinning at 70px, sliding
     13px of the nav under an opaque bar — it looked squashed as you scrolled.
     A skin that changes the height of either bar overrides these three and
     nothing else. --stick-nav is the top bar's height minus its bottom border,
     so the nav tucks under the rule instead of doubling it. */
  --stick-nav:70px;      /* .addie-nav pin: .top is 71px incl. a 1px border */
  --stick-head:126px;    /* .section-head pin: 71 + 45px nav + 10px breathing */
  --stick-anchor:132px;  /* jump targets: the same stack + 16px */
}
html[data-theme="dark"] {
  --bg:#121218; --surface:#1A1A22; --ink:#ECECF2; --ink-2:#C9C9D3; --soft:#AEAFBE; --faint:#888997;
  --line:#2A2A33; --hair:#1F1F27;
  --accent:#7C75F0; --accent-2:#9C96F7; --accent-soft:#22203A; --accent-ink:#D6D2FB;
  /* Text sitting ON the --accent FILL (the A/D/D/I/E chips, the active
     category tab). This was hardcoded #fff in 20 rules, which works on a
     light theme's deep accent and fails on every dark theme's light one —
     white on Night Class's gold measured 2.10. Each theme names its own. */
  --on-accent:#14110D;   /* 5.07:1 */
  --ok:#57D89B; --warn:#F2A877;
}

* { box-sizing:border-box; }
html { scroll-behavior:smooth; font-size:calc(100% * var(--f-scale, 1)); }
body {
  margin:0; background:var(--bg); color:var(--ink);
  font-family:var(--f-sans); font-size:calc(17px * var(--f-scale, 1)); line-height:1.6;
  -webkit-font-smoothing:antialiased;
  transition:background-color .3s ease, color .3s ease;
}
.wrap { width:100%; max-width:var(--maxw); margin-inline:auto; padding-inline:32px; }
a { color:inherit; text-decoration:none; }
h1,h2,h3,h4 { margin:0; line-height:1.08; letter-spacing:var(--f-head-tracking, -0.02em); font-family:var(--f-display); font-weight:var(--f-head-weight, 600); }
.eyebrow { font-size:0.72rem; font-weight:600; letter-spacing:0.16em; text-transform:uppercase; color:var(--accent-text); }
.ph-label { font-family:var(--f-mono); font-size:0.72rem; letter-spacing:0.16em; text-transform:uppercase; color:var(--faint); }

/* ---------------------------------------------------------------------------
   Top bar
   --------------------------------------------------------------------------- */
.top { position:sticky; top:0; z-index:50; background:color-mix(in srgb, var(--surface) 86%, transparent); backdrop-filter:blur(12px); border-bottom:1px solid var(--line); }
.top .wrap { display:flex; align-items:center; gap:14px; height:70px; position:relative; }
.brand { display:flex; align-items:center; gap:12px; white-space:nowrap; }
.brand-mark { height:30px; width:auto; color:var(--accent-text); flex:none; }
.brand .name { font-family:var(--f-display); font-weight:700; font-size:1.1rem; letter-spacing:-0.02em; }
.brand .role { font-size:0.88rem; color:var(--soft); padding-inline-start:12px; border-inline-start:1px solid var(--line); }
.top .spacer { flex:1; }
.topbar-tools { display:flex; align-items:center; gap:18px; }
.topbar-toggles { display:flex; align-items:center; gap:10px; }
.nav-link { font-family:var(--f-sans); font-weight:600; font-size:0.92rem; color:var(--ink); padding:6px 4px; border-radius:8px; }
.nav-link:hover, .nav-link.is-current { color:var(--accent-text); }

/* Hamburger: hidden on desktop, revealed when the top bar collapses.
   Scoped to .top so it beats .iconbtn's display:grid regardless of source order. */
.top .hamburger { display:none; flex:none; }
.hamburger .i-close { display:none; }
.hamburger[aria-expanded="true"] .i-bars { display:none; }
.hamburger[aria-expanded="true"] .i-close { display:block; }

.seg { display:inline-flex; background:var(--bg); border:1px solid var(--line); border-radius:10px; padding:3px; }
.seg a, .seg button { appearance:none; border:0; cursor:pointer; font-family:var(--f-sans); font-size:0.8rem; font-weight:600; color:var(--soft); background:transparent; padding:6px 13px; border-radius:7px; transition:color .15s; }
.seg a[aria-current="true"], .seg button[aria-pressed="true"] { background-color:var(--accent); color:#fff; }

.iconbtn { appearance:none; cursor:pointer; width:38px; height:38px; border-radius:10px; border:1px solid var(--line); background:var(--surface); color:var(--soft); display:grid; place-items:center; transition:background .2s, color .2s, border-color .2s; }
.iconbtn:hover { color:var(--accent-text); border-color:var(--accent); }
.iconbtn svg { width:18px; height:18px; }
.i-sun { display:none; }
.i-moon { display:block; }
html[data-theme="dark"] .i-sun { display:block; }
html[data-theme="dark"] .i-moon { display:none; }

.cta, .btn { font-family:var(--f-sans); font-weight:600; font-size:0.92rem; cursor:pointer; border-radius:10px; padding:11px 22px; border:1.5px solid transparent; display:inline-flex; align-items:center; gap:9px; white-space:nowrap; transition:background .2s, color .2s, transform .15s, border-color .2s; }
.cta, .btn-primary { background:var(--cta); color:var(--cta-ink, #fff); }
.cta:hover, .btn-primary:hover { background:var(--cta-2); color:var(--cta-ink, #fff); transform:translateY(-1px); }
.btn-ghost { border-color:var(--line); color:var(--ink); background:var(--surface); }
.btn-ghost:hover { border-color:var(--accent); color:var(--accent-text); }
.btn-block { width:100%; justify-content:center; padding:14px; font-size:1rem; }

/* ---------------------------------------------------------------------------
   Hero
   --------------------------------------------------------------------------- */
.hero { padding:76px 0 34px; }
.hero .wrap { display:grid; grid-template-columns:1fr 270px; gap:56px; align-items:center; }
.hero .eyebrow { margin-bottom:20px; }
.hero h1 { font-size:clamp(2.6rem,5.4vw,4.2rem); font-weight:600; letter-spacing:-0.03em; line-height:1.0; }
.hero h1 .accent-text { color:var(--accent-text); }
.hero .lede { margin:26px 0 0; font-size:1.12rem; color:var(--ink-2); max-width:50ch; }
.hero-actions { display:flex; gap:12px; margin-top:30px; flex-wrap:wrap; }
.caps { margin-top:34px; padding-top:20px; border-top:1px solid var(--line); }
.caps .caps-label { font-size:0.68rem; font-weight:600; letter-spacing:0.14em; text-transform:uppercase; color:var(--soft); margin-bottom:8px; }
.caps .list { color:var(--ink-2); font-size:0.98rem; }
.caps .list .dot { color:var(--accent-text); margin:0 9px; }
.headshot { aspect-ratio:var(--ar-square); border-radius:18px; background:repeating-linear-gradient(135deg, var(--hair) 0 14px, var(--bg) 14px 15px); border:1px solid var(--line); display:grid; place-items:center; overflow:hidden; }
.headshot img { width:100%; height:100%; object-fit:cover; }

/* ---- About page ---- */
.about-hero { padding-bottom:20px; }
.about-body { max-width:62ch; margin-top:8px; }
.about-body p { margin:0 0 1.1em; font-size:1.12rem; line-height:1.7; color:var(--ink-2); }
.about-body p:last-child { margin-bottom:0; }
/* Rich-text prose fields (the intro ledes + About body) now hold real block
   markup — reset the spacing of the paragraphs/lists/quotes they contain so a
   single-line intro still looks like one, and lists/indents render tidily. */
.about-body blockquote, .lede blockquote { margin-block:0 1.1em; margin-inline-start:1.4em; margin-inline-end:0; }
.lede p { margin:0 0 .7em; }
.lede p:last-child { margin-bottom:0; }
.lede ul, .lede ol { margin:.5em 0 .7em; padding-inline-start:1.4em; }
.lede li { margin-bottom:.25em; }

/* ---------------------------------------------------------------------------
   Selected work — category tabs + project rows
   --------------------------------------------------------------------------- */
.work { padding:48px 0 40px; }
.work-head { margin-bottom:22px; }
.work-head h2 { font-size:clamp(1.9rem,3.8vw,2.7rem); font-weight:600; margin-bottom:18px; }
.tabs { display:flex; gap:8px; flex-wrap:wrap; }
.tabs .tab { font-family:var(--f-sans); font-size:0.88rem; font-weight:600; color:var(--soft); background:var(--surface); border:1px solid var(--line); padding:9px 17px; border-radius:999px; cursor:pointer; transition:all .18s; }
.tabs .tab:hover { border-color:var(--accent); color:var(--accent-text); }
.tabs .tab.active { background:var(--accent); border-color:var(--accent); color:var(--on-accent); }
.tabs .tab .n { opacity:.6; margin-inline-start:6px; font-variant-numeric:tabular-nums; }

.catgroup { padding-top:34px; }
.catgroup .cat-label { display:flex; align-items:center; gap:14px; margin-bottom:20px; }
.catgroup .cat-label h3 { font-size:1.3rem; font-weight:600; }
.catgroup .cat-label .rule { flex:1; height:1px; background:var(--line); }
.catgroup .cat-label .count { font-size:0.78rem; color:var(--soft); font-weight:600; }

.proj-list { display:flex; flex-direction:column; gap:16px; }
.proj-row { display:flex; flex-direction:row; align-items:stretch; text-decoration:none; color:inherit; background:var(--surface); border:1px solid var(--line); border-radius:16px; overflow:hidden; box-shadow:0 1px 3px rgba(22,22,31,0.05),0 2px 6px rgba(22,22,31,0.03); transition:transform .18s ease, box-shadow .22s ease, border-color .2s; position:relative; }
.proj-row:not(.placeholder):hover { transform:translateY(-4px); box-shadow:0 18px 36px -18px color-mix(in srgb,var(--accent) 60%,transparent); border-color:color-mix(in srgb,var(--accent) 45%,var(--line)); }
.proj-row:not(.placeholder):active { transform:translateY(-1px); box-shadow:0 1px 3px rgba(22,22,31,0.05); transition-duration:.06s; }
.proj-row:not(.placeholder):hover .proj-arrow { color:var(--accent-text); }
.proj-row:not(.placeholder):hover .proj-arrow .ar { transform:translateX(6px); }
.proj-thumb { flex:none; width:38%; max-width:340px; aspect-ratio:var(--ar-video); background:var(--accent-soft); background-image:repeating-linear-gradient(135deg, transparent 0 15px, color-mix(in srgb,var(--accent) 6%,transparent) 15px 16px); display:grid; place-items:center; border-inline-end:1px solid var(--line); position:relative; }
.proj-thumb .ph { font-size:0.68rem; letter-spacing:0.16em; text-transform:uppercase; color:color-mix(in srgb,var(--accent) 50%,var(--faint)); font-family:var(--f-mono); }
.proj-thumb img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.tag-soon { position:absolute; top:13px; inset-inline-start:13px; display:inline-block; font-size:0.64rem; font-weight:700; letter-spacing:0.08em; text-transform:uppercase; background:var(--surface); color:var(--soft); border:1px solid var(--line); padding:5px 10px; border-radius:7px; white-space:nowrap; }
.proj-body { display:flex; flex-direction:column; gap:7px; padding:24px 28px; justify-content:center; flex:1; }
.proj-body .resource { font-size:0.7rem; font-weight:700; letter-spacing:0.1em; text-transform:uppercase; color:var(--accent-text); }
.proj-body h4 { font-size:1.3rem; font-weight:600; line-height:1.2; }
.proj-body .blurb { font-size:0.94rem; color:var(--soft); margin:0; line-height:1.55; }
.proj-footer { display:flex; align-items:center; gap:14px; margin-top:6px; }
.proj-arrow { font-size:0.86rem; font-weight:600; color:var(--ink); display:inline-flex; align-items:center; gap:7px; transition:color .2s; }
.proj-arrow .ar { color:var(--accent-text); display:inline-block; transition:transform .2s; }
.proj-row.placeholder .proj-thumb { opacity:0.5; }
.proj-row.placeholder .proj-body h4 { color:var(--soft); }
/* Draft preview (owner-only — never shown to a real visitor): same dimmed
   treatment as "Coming soon", but the tag is dashed to read as private/WIP
   rather than a public promise. */
.tag-soon.tag-draft { border-style:dashed; }
@media (prefers-reduced-motion: reduce){ .proj-row { transition:box-shadow .22s ease, border-color .2s; } .proj-row:not(.placeholder):hover { transform:none; } .proj-row:not(.placeholder):hover .proj-arrow .ar { transform:translateX(4px); } }

/* Dark mode: lift the resource-list contrast (blurb, "Coming soon" tag,
   thumbnail label, placeholder titles all read too faint on the dark surface). */
html[data-theme="dark"] .proj-body .blurb { color:var(--ink-2); }
html[data-theme="dark"] .proj-body .resource { color:var(--accent-ink); }
html[data-theme="dark"] .proj-thumb .ph { color:color-mix(in srgb,var(--accent) 70%,var(--ink-2)); }
html[data-theme="dark"] .proj-row.placeholder .proj-thumb { opacity:0.72; }
html[data-theme="dark"] .tag-soon {
  background:color-mix(in srgb,var(--surface) 65%,var(--bg));
  color:var(--ink); border-color:color-mix(in srgb,var(--ink) 28%,var(--line));
}

/* ---------------------------------------------------------------------------
   Contact
   --------------------------------------------------------------------------- */
.contact { padding:70px 0 50px; }
.contact .wrap { display:grid; grid-template-columns:0.9fr 1.1fr; gap:60px; }
.contact .intro .eyebrow { margin-bottom:14px; }
.contact .intro h2 { font-size:clamp(1.9rem,4vw,2.8rem); font-weight:600; margin-bottom:18px; }
.contact .intro h2 .accent-text { color:var(--accent-text); }
.contact .intro p, .contact .intro .lede-contact { color:var(--ink-2); max-width:40ch; }
.contact .intro .mail { margin-top:22px; font-weight:600; }
.contact .intro .mail a { color:var(--accent-text); border-bottom:1px solid transparent; padding-bottom:2px; }
.contact .intro .mail a:hover { border-color:var(--accent); }

.form { background:var(--surface); border:1px solid var(--line); border-radius:18px; padding:28px; display:grid; gap:16px; }
.form .row { display:grid; grid-template-columns:1fr 1fr; gap:16px; }
.field { display:grid; gap:7px; }
.field label { font-size:0.82rem; font-weight:600; color:var(--ink-2); }
.field input, .field select, .field textarea { font-family:inherit; font-size:1rem; color:var(--ink); background:var(--bg); border:1.5px solid var(--line); border-radius:10px; padding:12px 14px; width:100%; transition:border-color .15s, box-shadow .15s; }
.field input:focus, .field select:focus, .field textarea:focus { outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft); }
.field textarea { resize:vertical; min-height:110px; }
.field select { appearance:none; }
.form .submit { font-weight:600; font-size:0.95rem; background:var(--accent); color:var(--on-accent); border:0; border-radius:10px; padding:14px; cursor:pointer; transition:background .2s, opacity .2s; }
.form .submit:hover { background:var(--accent-2); }

/* Gate the contact "Send" button until every required field is valid — pure CSS,
   no JavaScript. This dims the button and blocks clicks while any field is
   incomplete or invalid; the fields' own `required`/`type=email` still stop a
   real submit. ".submit" only exists on the contact form, so login/reset are
   unaffected. */
.form:has(:invalid) .submit { opacity:0.4; pointer-events:none; }

.form-flash { border-radius:var(--radius-sm); padding:13px 16px; font-size:0.92rem; font-weight:500; border:1px solid transparent; }
.form-flash.is-success { background:color-mix(in srgb,var(--ok) 16%,var(--surface)); color:var(--ok); border-color:color-mix(in srgb,var(--ok) 40%,var(--line)); }
.form-flash.is-error { background:color-mix(in srgb,var(--warn) 16%,var(--surface)); color:var(--warn); border-color:color-mix(in srgb,var(--warn) 40%,var(--line)); }

/* Owner-only warning strip when data/store.json exists but can't be read
   (see store_corrupt() in includes/store.php + the banner in header.php). */
.store-corrupt-banner { background:var(--warn); color:#fff; padding:12px 20px; text-align:center; font-size:0.92rem; font-weight:600; }
.hp { position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden; }

/* ---------------------------------------------------------------------------
   Footer
   --------------------------------------------------------------------------- */
.foot { border-top:1px solid var(--line); margin-top:30px; }
.foot .wrap { display:flex; align-items:center; gap:20px; flex-wrap:wrap; padding-block:26px; font-size:0.86rem; color:var(--soft); }
.foot .spacer { flex:1; }
.foot a:hover { color:var(--ink); }
.foot .accent-text { color:var(--accent-text); font-weight:600; }
.foot .social-link { display:inline-grid; place-items:center; width:34px; height:34px; border-radius:8px; color:var(--soft); border:1px solid var(--line); transition:color .15s, border-color .15s; }
.foot .social-link:hover { color:var(--accent-text); border-color:var(--accent); }
.admin-link { font-size:0.82rem; color:var(--faint); border-bottom:1px dotted var(--line); transition:color .15s, border-color .15s; }
.admin-link:hover { color:var(--accent-text); border-color:var(--accent); }
.admin-flag { font-family:var(--f-display); font-size:0.72rem; font-weight:700; letter-spacing:0.04em; text-transform:uppercase; color:var(--accent-text); background:var(--accent-soft); border:1px solid color-mix(in srgb,var(--accent) 30%,var(--line)); padding:4px 10px; border-radius:999px; }

/* ===========================================================================
   RESOURCE DETAIL PAGE
   =========================================================================== */
.back { display:inline-flex; align-items:center; gap:8px; margin-top:28px; font-size:0.88rem; font-weight:600; color:var(--soft); transition:color .2s; }
.back:hover { color:var(--accent-text); }

.detail-head { padding:26px 0 36px; }
.detail-head h1 { font-size:clamp(2.4rem,5.5vw,4rem); font-weight:600; letter-spacing:-0.03em; max-width:18ch; }
.detail-head .summary { margin-top:22px; font-size:1.18rem; color:var(--ink-2); max-width:60ch; }
.specbar { margin-top:34px; display:grid; grid-template-columns:repeat(4,1fr); background:var(--surface); border:1px solid var(--line); border-radius:16px; overflow:hidden; }
.specbar .spec { padding:18px 22px; border-inline-end:1px solid var(--line); }
.specbar .spec:last-child { border-inline-end:0; }
.specbar .spec .spec-label { font-size:0.66rem; font-weight:600; letter-spacing:0.14em; text-transform:uppercase; color:var(--label-ink); margin-bottom:6px; }
.specbar .spec .spec-value { font-size:1rem; font-weight:500; color:var(--ink-2); }

/* Skills strip under the spec bar — cross-cutting competencies the project
   demonstrates, deliberately kept out of the ADDIE arc (it's about the maker,
   not the process). Reuses the ul.skills chip styling. */
.detail-skills { margin-top:16px; padding:2px 22px 0; display:flex; align-items:center; gap:8px 14px; flex-wrap:wrap; }
.detail-skills .ds-label { flex:none; font-size:0.66rem; font-weight:600; letter-spacing:0.14em; text-transform:uppercase; color:var(--label-ink); }

.plate { aspect-ratio:var(--ar-video); background:var(--accent-soft); background-image:repeating-linear-gradient(135deg, transparent 0 18px, color-mix(in srgb,var(--accent) 6%,transparent) 18px 19px); border:1px solid var(--line); border-radius:18px; display:grid; place-items:center; margin-top:8px; overflow:hidden; position:relative; }
.plate img { width:100%; height:100%; object-fit:cover; display:block; }

.prose { padding:58px 0 20px; }

/* ---------------------------------------------------------------------------
   ADDIE jump-nav — slim sticky sub-bar under the top bar (resource detail only).
   Lists only the phases that have content; scroll-spy adds .is-active.
   --------------------------------------------------------------------------- */
.addie-nav { position:sticky; top:var(--stick-nav); z-index:40; background:color-mix(in srgb, var(--surface) 88%, transparent); backdrop-filter:blur(12px); border-bottom:1px solid var(--line); }
.addie-nav .wrap { display:flex; align-items:center; gap:20px; }
.addie-nav .addie-nav-label { flex:none; font-size:0.64rem; font-weight:600; letter-spacing:0.14em; text-transform:uppercase; color:var(--faint); font-family:var(--f-mono); }
.addie-nav ul { list-style:none; margin:0; padding:0; display:flex; gap:2px; overflow-x:auto; scrollbar-width:none; }
.addie-nav ul::-webkit-scrollbar { display:none; }
.addie-nav a { display:flex; align-items:center; gap:9px; padding:13px 14px; color:var(--soft); font-weight:600; font-size:0.9rem; white-space:nowrap; position:relative; transition:color .18s; }
.addie-nav a .an-letter { flex:none; font-family:var(--f-mono); font-size:0.7rem; font-weight:700; width:22px; height:22px; border-radius:6px; display:grid; place-items:center; background:var(--bg); border:1px solid var(--line); color:var(--faint); transition:background .18s, border-color .18s, color .18s; }
.addie-nav a::after { content:""; position:absolute; left:14px; right:14px; bottom:0; height:2px; border-radius:2px 2px 0 0; background:var(--accent); transform:scaleX(0); transform-origin:left; transition:transform .2s ease; }
.addie-nav a:hover { color:var(--ink); }
.addie-nav a:hover .an-letter { border-color:var(--accent); color:var(--accent-text); }
.addie-nav a.is-active { color:var(--accent-text); }
.addie-nav a.is-active .an-letter { background:var(--accent); border-color:var(--accent); color:var(--on-accent); }
.addie-nav a.is-active::after { transform:scaleX(1); }

/* Land jump targets below both sticky bars (top bar + ADDIE nav). */
[id^="s-"], [id^="phase-"], #full-resource { scroll-margin-top:var(--stick-anchor); }

/* ---------------------------------------------------------------------------
   ADDIE phase dividers — centred chapter markers between phases. The jump-nav
   bar links to these. Letter chip echoes the bar; the wrap-up divider is
   letter-less (--plain) to signal it sits outside the ADDIE arc.
   --------------------------------------------------------------------------- */
.phase-divider { display:flex; align-items:center; gap:24px; margin:64px 0 8px; }
.phase-divider .pd-rule { flex:1; height:2px; border-radius:2px; background:color-mix(in srgb, var(--ink) 20%, transparent); }
.phase-divider .pd-label { display:inline-flex; align-items:center; gap:13px; flex:none; }
.phase-divider .pd-letter { flex:none; font-family:var(--f-mono); font-size:0.82rem; font-weight:700; width:30px; height:30px; border-radius:8px; display:grid; place-items:center; background:var(--accent-soft); color:var(--accent-ink); }
.phase-divider .pd-name { font-family:var(--f-display); font-weight:600; font-size:1.5rem; letter-spacing:-0.02em; color:var(--ink); }
/* Wrap-up divider: quieter, no letter chip, eyebrow-styled label + finer rule. */
.phase-divider--plain { margin-top:56px; }
.phase-divider--plain .pd-rule { height:1px; background:color-mix(in srgb, var(--ink) 12%, transparent); }
.phase-divider--plain .pd-name { font-family:var(--f-sans); font-size:0.74rem; font-weight:600; letter-spacing:0.16em; text-transform:uppercase; color:var(--soft); }
/* A phase divider that opens a prose block shouldn't sit under a doubled rule. */
.prose .wrap > .phase-divider:first-child { margin-top:8px; }
.phase-divider + .detail-section { border-top:0; padding-top:20px; }
.detail-section { display:grid; grid-template-columns:240px 1fr; gap:44px; padding:34px 0; border-top:1px solid var(--line); }
.detail-section:first-child { border-top:0; padding-top:0; }
.detail-section .section-head { position:sticky; top:var(--stick-head); align-self:start; }
.detail-section .section-head h2 { font-size:1.4rem; font-weight:600; }
.detail-section .section-head h2 .accent-text { color:var(--accent-text); }
.detail-section .section-body { font-size:1.08rem; color:var(--ink-2); max-width:64ch; }
.detail-section .section-body p { margin:0 0 14px; }
.detail-section .section-body p:last-child { margin-bottom:0; }
.detail-section .section-body strong { color:var(--ink); }

ul.clean, ol.clean { list-style:none; margin:0; padding:0; display:grid; gap:13px; }
ul.clean li, ol.clean li { display:grid; grid-template-columns:auto 1fr; gap:14px; align-items:baseline; }
ul.clean li::before, ol.clean li::before { content:""; width:7px; height:7px; border-radius:2px; background:var(--accent); transform:translateY(-2px) rotate(45deg); }
ul.skills { list-style:none; margin:0; padding:0; display:flex; flex-wrap:wrap; gap:9px; }
ul.skills li { font-size:0.86rem; font-weight:600; color:var(--accent-ink); background:var(--accent-soft); border:1px solid color-mix(in srgb,var(--accent) 18%,var(--line)); padding:8px 14px; border-radius:8px; }

.swatches { display:flex; flex-wrap:wrap; gap:14px; }
.swatch { width:96px; }
.swatch .chip { height:62px; border-radius:10px; border:1px solid color-mix(in srgb,var(--ink) 12%,transparent); }
.swatch .hex { font-size:0.74rem; font-weight:600; color:var(--soft); margin-top:8px; font-variant-numeric:tabular-nums; font-family:var(--f-mono); }

/* ---- Visual style section: labeled colors + labeled type samples --------- */
.vstyle-group + .vstyle-group { margin-top:28px; }
.vstyle-label { display:block; font-size:0.68rem; font-weight:700; letter-spacing:0.14em; text-transform:uppercase; color:var(--faint); font-family:var(--f-mono); margin-bottom:14px; }
.swatch-label { font-size:0.78rem; font-weight:600; color:var(--ink-2); margin-bottom:8px; }
.font-samples { display:flex; flex-wrap:wrap; gap:18px; }
.font-sample { width:180px; padding:16px; border:1px solid var(--line); border-radius:var(--radius-sm); background:var(--surface); }
.font-sample-glyph { font-size:2.2rem; line-height:1; color:var(--ink); }
.font-sample-name { margin-top:10px; font-size:0.74rem; font-weight:600; color:var(--soft); }
/* Type-style box: Aa (at its true size) on the left; the specific tag + px size
   stacked to its right; family name below. The general label is the group header.
   The Aa row is a FIXED height per group (--fs-top-h, set to the group's largest
   style), so the tag/size and name line up across boxes — only the Aa changes. */
/* TOP-aligned, not bottom. Bottom alignment sat the tag on a common baseline
   across boxes, but inside a box it left the Aa's top floating anywhere from
   6px above to 13px below the tag beside it, depending on the style's size.
   Aligning tops fixes the in-box relationship AND still lines the tags up
   across boxes, because they now all start at the same y. */
.fs-top { display:flex; align-items:flex-start; gap:12px; height:var(--fs-top-h, 2.6rem); }
.fs-meta { display:flex; flex-direction:column; line-height:1.15; }
.fs-tag { font-size:0.9rem; font-weight:700; color:var(--ink); font-family:var(--f-display); }
.fs-size { font-family:var(--f-mono); font-size:0.68rem; font-weight:500; color:var(--faint); }
/* On phones, fit two type-style boxes per line (a 3-item Header row would
   otherwise cram). The groups + row are forced full-width so a real 2-column
   grid can resolve (a % width wouldn't — the group is shrink-to-fit). Drop the
   bracket connectors (they don't map onto a grid); long names wrap. */
@media (max-width:520px) {
  /* Type styles on phones: a uniform 2-per-row grid. Every style box is one
     equal-width cell — whether it's a solo-label box or one item in a group —
     so boxes always align 2-across instead of stacking full-width. Selectors
     are extra-specific to win over the base .font-samples / .vstyle-conn-* rules
     that appear later in the file. */
  .vstyle-group .font-samples {
    display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr);
    gap:18px 14px; align-items:start;
  }
  .font-samples > .vstyle-conn-group,
  .font-samples > .font-sample { width:auto; min-width:0; margin:0; align-items:stretch; }
  .font-samples .font-sample { width:auto; min-width:0; }
  /* Solo-label group: label above its own box, left-aligned, wraps freely.
     A GROUP of 2+ keeps the centred label + bracket (below). */
  .font-samples > .vstyle-conn-group:not(.vstyle-conn-group--multi) .vstyle-conn-head { align-items:flex-start; }
  .font-samples > .vstyle-conn-group:not(.vstyle-conn-group--multi) .vstyle-conn-stem { display:none; }
  .font-samples > .vstyle-conn-group .vstyle-conn-label { white-space:normal; }
  /* A multi-font group spans the full row and keeps its own inner 2-col grid. */
  .font-samples > .vstyle-conn-group--multi { grid-column:1 / -1; }
  .font-samples .vstyle-conn-row--fonts { display:grid; grid-template-columns:repeat(min(var(--n,2), 2), minmax(0,1fr)); gap:14px; width:100%; }
  /* The bracket, kept on phones. It was dropped here because the row becomes a
     grid and the desktop rule's fixed 90px insets no longer land on anything.
     With --n (the item count, emitted by Resource.php) the outer cell centres
     are exact: (100% - (n-1)*gap) / 2n from each edge. */
  .font-samples .vstyle-conn-row--fonts::before {
    left:calc((100% - (min(var(--n,2), 2) - 1) * 14px) / (2 * min(var(--n,2), 2)));
    right:calc((100% - (min(var(--n,2), 2) - 1) * 14px) / (2 * min(var(--n,2), 2)));
  }
  /* Colors on phones: a 3-per-row grid. Standalone swatches are one cell each; a
     shared-label group (e.g. the 3 Secondary Accents) spans the full row and
     keeps its swatches together under one label. */
  .vstyle-group .swatches { display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:16px 14px; align-items:start; }
  /* Every swatch fills its grid cell — the base `.swatch { width:96px }` would
     otherwise leave it left-aligned in a wider cell, putting the swatch's
     centre ~30px off the cell centre the bracket's calc() aims at. The `>`
     selector alone missed the ones nested inside a connector row. */
  .swatches .swatch { width:auto; min-width:0; }
  .swatches > .vstyle-conn-group { grid-column:1 / -1; }
  .swatches .vstyle-conn-row--colors { display:grid; grid-template-columns:repeat(min(var(--n,3), 3), minmax(0,1fr)); gap:14px; width:100%; }
  /* Same treatment as the type bracket above: --n makes the rule end on the
     centre of the first and last swatch whatever the count. */
  .swatches .vstyle-conn-row--colors::before {
    left:calc((100% - (min(var(--n,3), 3) - 1) * 14px) / (2 * min(var(--n,3), 3)));
    right:calc((100% - (min(var(--n,3), 3) - 1) * 14px) / (2 * min(var(--n,3), 3)));
  }
}

/* ---- Bracket-grouped swatches / type samples: 2+ entries sharing one label
   get a single centered label wired down to each item's top-center with an
   orthogonal connector (echoes the node-diagram in the site's own logo mark). */
.swatches, .font-samples { align-items:flex-end; }
.vstyle-conn-group { display:flex; flex-direction:column; align-items:center; }
.vstyle-conn-head { display:flex; flex-direction:column; align-items:center; }
.vstyle-conn-label { font-size:0.78rem; font-weight:600; color:var(--ink-2); margin-bottom:8px; white-space:nowrap; }
.vstyle-conn-stem { width:1px; height:14px; background:var(--conn-line); }
.vstyle-conn-row { position:relative; display:flex; gap:14px; padding-top:14px; }
.vstyle-conn-row::before { content:""; position:absolute; top:0; height:1px; background:var(--conn-line); }
.vstyle-conn-row--colors::before { left:48px; right:48px; }
.vstyle-conn-row--fonts::before { left:90px; right:90px; }
.vstyle-conn-row .swatch, .vstyle-conn-row .font-sample { position:relative; }
.vstyle-conn-row .swatch::before, .vstyle-conn-row .font-sample::before { content:""; position:absolute; top:-14px; left:50%; width:1px; height:14px; background:var(--conn-line); transform:translateX(-50%); }

.feature { padding:40px 0; }
.feature .feature-head { display:flex; align-items:baseline; gap:12px; margin-bottom:24px; }
.feature .feature-head h2 { font-size:clamp(1.6rem,3.2vw,2.2rem); font-weight:600; }
.feature .feature-head h2 .accent-text { color:var(--accent-text); }
.feature .feature-head .note { font-size:0.78rem; color:var(--faint); font-weight:600; font-family:var(--f-mono); }

/* ---- Storyboards table (rendered by table.js — display only) ---- */
/* overflow-y:auto + max-height, not just overflow-x — a sticky header needs
   ITS scrolling ancestor to be the box that's actually scrolling. Before this,
   .tbl-scroll only declared overflow-x, but a browser can't leave overflow-y
   at "visible" once overflow-x isn't — the spec computes it to auto too — so
   this element was ALREADY the sticky header's reference frame, just one with
   unbounded height that never scrolls on its own (the PAGE scrolls it, whole).
   A sticky child of a box that never scrolls independently just sits wherever
   it lands and never moves again — which is exactly the "stuck mid-table,
   not tracking scroll" bug this fixes: bounding the height turns this into a
   real internal scrollport, and the header's position:sticky (top:0 below,
   not var(--stick-head) — it sticks to THIS box now, not the page) finally
   has a genuine scroll to stick within. Border/radius/clip moved here too
   (was on .sb-table) for the same reason: overflow:hidden on the <table>
   itself sat BETWEEN the header and this box, making the table the sticky
   reference instead — same failure, one level down. */
.tbl-scroll { overflow-x:auto; overflow-y:auto; max-height:65vh; border:1px solid var(--line); border-radius:14px; }
/* Same breakpoint .tbl-scroll disappears at (below) — mobile already reads a
   storyboard one screen at a time via the pager (.sb-pager), which has no
   "long scroll" to skip past, so these links have nothing to do there. */
.sb-skipnav { display:inline-block; font-size:.86rem; color:var(--soft); text-decoration:none; margin:0 0 8px; }
.sb-skipnav:hover { color:var(--accent-text); text-decoration:underline; }
.sb-skipnav.is-bottom { margin:8px 0 0; }
/* table-layout:fixed makes column width a rule, not a guess: the first column
   (a short label — "1.0 Welcome", "Critical") gets a flat 12%, and every
   other column auto-splits whatever's left evenly, however many there are —
   that split is the fixed-layout algorithm's own behavior for a column with
   no declared width, not something computed per row. Was auto-layout, which
   sized the first column to whatever fit its content on one line (helped by
   the white-space:nowrap two rules down) and starved the rest.

   --sb-cols (set inline wherever a .sb-table is built — Resource.php's fixed
   3-column issues table, table.js's owner-editable storyboard) drives the
   min-width floor below, so a wide storyboard still grows past 100% and
   scrolls (.tbl-scroll) instead of every column getting crushed as more get
   added. Falls back to 5 if a caller ever forgets to set it — this project's
   original column count, not a real limit. */
.sb-table { width:100%; table-layout:fixed; border-collapse:collapse; font-size:0.95rem; background:var(--surface); min-width:calc(90px + (var(--sb-cols, 5) - 1) * 150px); }
.sb-table th, .sb-table td { text-align:start; padding:14px 16px; border-bottom:1px solid var(--line); border-inline-end:1px solid var(--line); vertical-align:top; overflow-wrap:break-word; }
.sb-table th:last-child, .sb-table td:last-child { border-inline-end:0; }
.sb-table thead th, .sb-table tr:first-child th,
.bld-grid thead th, .bld-grid tr:first-child th { position:sticky; top:0; z-index:2; }
.sb-table thead th, .sb-table tr:first-child th { background:var(--accent-soft); font-size:0.66rem; font-weight:700; letter-spacing:0.1em; text-transform:uppercase; color:var(--accent-ink); }
.sb-table tbody td:first-child, .sb-table tr td:first-child { font-weight:600; color:var(--accent-text); }
/* fixed layout only reads a column's width off the FIRST ROW's cells — every
   .sb-table's first row is th (the header), not td, so the width has to land
   on thead th:first-child too or the browser has nothing to size column 1
   from and splits it evenly with the rest, same as before this fix. Kept
   apart from the td rule above so this doesn't also hand the header cell the
   td's font-weight/color and fight the header styling one line up. */
.sb-table tbody td:first-child, .sb-table tr td:first-child,
.sb-table thead th:first-child, .sb-table tr:first-child th:first-child { width:12%; }
.sb-table tr:last-child td { border-bottom:0; }

/* ---- Carousel (rendered by media.js) ---- */
/* Skip link. Moved off-screen with transform rather than display:none or
   visibility:hidden — both of those remove it from the tab order, which is the
   one thing it exists to be in. inset-inline-start so it lands on the correct
   edge in RTL. */
.skip-link { position:absolute; top:0; inset-inline-start:0; z-index:200;
  transform:translateY(-130%); transition:transform .15s ease;
  background:var(--surface); color:var(--ink); text-decoration:none;
  border:2px solid var(--accent); border-start-start-radius:0; border-end-end-radius:var(--radius-sm);
  padding:10px 18px; font-weight:650; font-size:0.92rem; }
.skip-link:focus { transform:translateY(0); }
@media (prefers-reduced-motion:reduce) { .skip-link { transition:none; } }
.bld-co-alt { flex:1 1 auto; min-width:0; font-size:.82rem; padding:5px 8px; }
.sb-actions { display:flex; justify-content:center; margin-top:14px; }
.sb-download-btn { font-size:0.85rem; }
.carousel-empty { border:1px dashed var(--line); border-radius:var(--radius); padding:40px; text-align:center; color:var(--soft); background:var(--bg); }
.carousel-empty code { display:inline-block; margin-top:10px; font-family:var(--f-mono); font-size:0.82rem; background:var(--surface); border:1px solid var(--line); padding:6px 12px; border-radius:8px; color:var(--accent-text); }
/* Peek carousel. --car-slide is the CURRENT slide's share of the stage width;
   the neighbours occupy the remainder, half on each side, clipped by the stage.
   It is also what sets the stage's HEIGHT: slides are 16:9, so 62% width means
   62% of the old height — the carousel used to stand as tall as the resource
   embed above it, which gave a strip of screenshots the same weight as the work
   itself. media.js reads this property to compute the track offset, so this
   declaration is the single source of truth and a skin can retune it. */
.carousel-stage { --car-slide:62%; --car-side-scale:.86; position:relative; border-radius:var(--radius); overflow:hidden; border:1px solid var(--line); background:var(--bg); }
.carousel-track { display:flex; align-items:center; transition:transform .45s cubic-bezier(.22,.61,.36,1); }
/* The scale is visual only — it never changes the flex layout, so the track
   maths stays a function of --car-slide alone and the stage height is set by
   the current slide rather than wobbling as neighbours grow and shrink. */
.carousel-slide { flex:0 0 var(--car-slide); margin:0; aspect-ratio:var(--ar-video); display:grid; place-items:center;
  transform:scale(var(--car-side-scale)); opacity:.45;
  transition:transform .45s cubic-bezier(.22,.61,.36,1), opacity .45s ease; }
.carousel-slide.is-current { transform:scale(1); opacity:1; }
.carousel-slide img { width:100%; height:100%; object-fit:cover; }
/* One image has no neighbours to make room for, so it takes the full stage. */
.carousel-stage.is-single { --car-slide:100%; --car-side-scale:1; }
.carousel-arrow { position:absolute; top:50%; transform:translateY(-50%); width:44px; height:44px; border-radius:10px; border:1.5px solid var(--line); background:color-mix(in srgb,var(--surface) 88%,transparent); backdrop-filter:blur(6px); color:var(--ink); font-size:1.1rem; cursor:pointer; display:grid; place-items:center; transition:border-color .2s,color .2s; }
.carousel-arrow:hover { border-color:var(--accent); color:var(--accent-text); }
.carousel-arrow.prev { inset-inline-start:14px; }
.carousel-arrow.next { inset-inline-end:14px; }
@media (max-width:760px) { .carousel-stage { --car-slide:84%; --car-side-scale:.9; } }
@media (prefers-reduced-motion:reduce) { .carousel-track, .carousel-slide { transition:none; } }
.carousel-dots { display:flex; gap:8px; justify-content:center; margin-top:16px; }
.carousel-dot { width:9px; height:9px; border-radius:50%; border:0; padding:0; cursor:pointer; background:var(--line); transition:background-color .2s, transform .2s; }
.carousel-dot.is-on { background:var(--accent); transform:scale(1.25); }
.carousel-counter { text-align:center; margin-top:10px; font-family:var(--f-mono); font-size:0.78rem; color:var(--faint); }
.carousel-upload { display:flex; justify-content:center; margin-top:14px; }
.carousel-add-btn { font-size:0.85rem; }

/* ---- Full resource (rendered by media.js) ---- */
.fr-controls { display:flex; gap:10px; flex-wrap:wrap; margin-bottom:12px; }
.fr-hint { font-size:0.82rem; color:var(--faint); margin-bottom:16px; }
.fr-frame { position:relative; aspect-ratio:var(--ar-video); border-radius:18px; overflow:hidden; border:1px solid var(--line); background:var(--bg); }
.fr-frame iframe { width:100%; height:100%; border:0; display:block; }
/* A PDF gets a page-shaped frame, not a widescreen one — a portrait page in a
   16:9 box is what made the browser's own viewer blow the page up. Capped
   against the viewport so a long document never pushes the rest of the page
   off-screen on a laptop. */
.fr-frame.is-pdf { aspect-ratio:1 / 1.2; max-height:82vh; }
@media (max-width:700px) { .fr-frame.is-pdf { aspect-ratio:1 / 1.35; } }
/* A self-hosted video plays in a real <video>, not a frame — same box, so the
   section keeps its shape whichever way a resource is presented. */
.fr-frame video { width:100%; height:100%; display:block; background:#000; object-fit:contain; }
/* Link-only: for things no browser renders in place (.pptx) and for hosts that
   refuse to be framed. An honest button beats an empty rectangle. */
.fr-frame.is-download { display:grid; place-items:center; background:var(--accent-soft); background-image:repeating-linear-gradient(135deg, transparent 0 18px, color-mix(in srgb,var(--accent) 6%,transparent) 18px 19px); }
.fr-download { text-align:center; padding:24px; }
.fr-frame.is-empty { display:grid; place-items:center; background:var(--accent-soft); background-image:repeating-linear-gradient(135deg, transparent 0 18px, color-mix(in srgb,var(--accent) 6%,transparent) 18px 19px); }

/* ===========================================================================
   LOGIN PAGE
   =========================================================================== */
.login { padding:64px 0 24px; min-height:60vh; }
.login-card { max-width:430px; margin:0 auto; background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); box-shadow:0 1px 3px rgba(22,22,31,0.05); padding:36px 34px; }
.login-card h1 { font-size:1.6rem; margin:0 0 6px; }
.login-note { color:var(--soft); margin:0 0 24px; font-size:0.95rem; }
.login-card .form { background:transparent; border:0; padding:0; gap:16px; }
.login-or { display:flex; align-items:center; gap:14px; margin:22px 0; color:var(--faint); font-size:0.78rem; font-weight:600; letter-spacing:0.08em; text-transform:uppercase; }
.login-or::before, .login-or::after { content:""; flex:1; height:1px; background:var(--line); }
.biometric-btn { width:100%; justify-content:center; padding:14px; font-size:0.98rem; gap:11px; }
.biometric-btn svg { width:20px; height:20px; }
.login-links { display:flex; justify-content:space-between; align-items:center; margin-top:18px; font-size:0.86rem; }
.login-links a { color:var(--soft); }
.login-links a:hover { color:var(--accent-text); }
.text-link { background:none; border:0; padding:0; cursor:pointer; font-family:inherit; font-size:inherit; color:var(--soft); }
.text-link:hover { color:var(--accent-text); }

/* ===========================================================================
   SETTINGS PANEL (owner-only) — Phase 2 shell, styled now
   =========================================================================== */
.settings-overlay { position:fixed; inset:0; z-index:90; background:rgba(15,15,25,0.45); backdrop-filter:blur(2px); display:none; }
.settings-overlay.open { display:block; }
/* The panel itself always docks to the physical right edge of the viewport
   and slides in from there, regardless of language — that's a fixed piece of
   app chrome (like a browser dev-tools panel), not something reading
   direction should flip. Its CONTENT, though, is now fully localized
   (including RTL) — see the [dir="rtl"] block near the end of this file. */
.settings-panel { position:fixed; top:0; right:0; height:100%; width:380px; max-width:92vw; z-index:91; background:var(--surface); border-left:1px solid var(--line); box-shadow:-20px 0 50px -20px rgba(0,0,0,0.3); transform:translateX(100%); transition:transform .28s cubic-bezier(.22,.61,.36,1); display:flex; flex-direction:column; }
.settings-panel.open { transform:translateX(0); }
.settings-panel .sp-head { display:flex; align-items:center; justify-content:space-between; padding:20px 22px; border-bottom:1px solid var(--line); }
.settings-panel .sp-head h2 { font-size:1.15rem; }
.settings-panel .sp-body { padding:22px; overflow-y:auto; display:grid; gap:26px; flex:1 1 auto; min-height:0; align-content:start; }

/* ===========================================================================
   Responsive
   =========================================================================== */
@media (max-width:860px) {
  .hero .wrap { grid-template-columns:1fr; gap:26px; }
  /* Compact circular avatar, top-left — a profile chip that doesn't shove the
     headline down the page the way a full 220px block did. */
  .headshot { max-width:132px; width:132px; order:-1; justify-self:start; border-radius:50%; aspect-ratio:var(--ar-square); }
  .contact .wrap { grid-template-columns:1fr; gap:34px; }
  .proj-row { flex-direction:column; }
  .proj-thumb { width:100%; max-width:none; aspect-ratio:var(--ar-video); border-inline-end:0; border-bottom:1px solid var(--line); }
  .tabs { flex-wrap:nowrap; overflow-x:auto; -webkit-overflow-scrolling:touch; padding-bottom:4px; scrollbar-width:none; }
  .tabs::-webkit-scrollbar { display:none; }
  .tabs .tab { flex:0 0 auto; }
  .specbar { grid-template-columns:1fr 1fr; }
  .specbar .spec:nth-child(2) { border-inline-end:0; }
  .specbar .spec:nth-child(1), .specbar .spec:nth-child(2) { border-bottom:1px solid var(--line); }
  .detail-section { grid-template-columns:1fr; gap:14px; }
  .detail-section .section-head { position:static; }
  .rec-switch-bar { position:static; }
}

/* ---- Collapse the top bar into a hamburger menu ------------------------------
   Fires early enough that the brand + tools + CTA never overflow / cause the
   horizontal scroll the full bar did. Tools (About, language, theme, settings)
   drop into a panel under the bar; the CTA + hamburger stay in the bar. */
@media (max-width:760px) {
  .brand .role { display:none; }
  .top .hamburger { display:grid; }
  .top .wrap { gap:10px; }
  .cta { padding:9px 16px; }
  .topbar-tools {
    display:none;
    position:absolute; top:calc(100% + 9px); right:0;
    flex-direction:row; align-items:center; justify-content:space-between; gap:14px;
    min-width:min(280px, 86vw);
    padding:10px 14px; background:var(--surface);
    border:1px solid var(--line); border-radius:14px;
    box-shadow:0 18px 44px rgba(22,22,31,0.18);
  }
  .topbar-tools[data-open="true"] { display:flex; }
  .topbar-tools .nav-link { order:-1; padding:10px 12px; border-radius:10px; font-size:1rem; }
  .topbar-tools .nav-link:hover { background:var(--bg); }
  .topbar-toggles { gap:8px; }
  /* Findings: reveal the toggle (JS has already collapsed the panel). */
  .findings-toggle { display:flex; }
}
@media (max-width:520px) {
  .form .row { grid-template-columns:1fr; }
  .brand .role { display:none; }
  .addie-nav .addie-nav-label { display:none; }
  .addie-nav a .an-label { display:none; }
  .addie-nav a { padding:11px 10px; }
  /* Phone gets a tighter gap under the bars, not a shorter stack — the nav is
     the same height with its labels hidden. Expressed as a delta off the token
     so a skin only ever overrides --stick-anchor once. */
  [id^="s-"], [id^="phase-"], #full-resource { scroll-margin-top:calc(var(--stick-anchor) - 14px); }
  .phase-divider { gap:16px; margin:44px 0 6px; }
  .phase-divider .pd-name { font-size:1.25rem; }
}

/* ===========================================================================
   RESOURCE BUILDER (owner-only) — lives inside the Settings panel
   =========================================================================== */

/* The panel grows when the builder is active so the form has room to breathe. */
.settings-panel { transition:transform .28s cubic-bezier(.22,.61,.36,1), width .32s cubic-bezier(.22,.61,.36,1); }
.settings-panel[data-builder-active="true"] { width:640px; }

/* Cross-fade between the settings home and the builder form. */
.sp-body { transition:opacity .15s ease; }
.sp-body.bld-fade-out { opacity:0; }
.bld-fade { animation:bldFadeIn .22s ease both; }
.bld-fade-out { opacity:0 !important; transition:opacity .13s ease; }
@keyframes bldFadeIn { from { opacity:0; transform:translateY(4px); } to { opacity:1; transform:none; } }
@media (prefers-reduced-motion:reduce) {
  .settings-panel, .sp-body, .bld-fade { transition:none !important; animation:none !important; }
}

/* ---- Settings-home: the Resources picker block --------------------------- */
.bld-home-block { display:grid; gap:12px; }
.bld-home-block > h3 { font-size:.95rem; }
#bld-resource-picker { display:grid; gap:10px; }
.bld-picker { width:100%; }
.bld-create { justify-self:start; }

/* ---- Builder header (replaces the gear-row contents) --------------------- */
.sp-body.bld-mode { padding-bottom:96px; }      /* clear the sticky save bar */
.bld-head-top { display:flex; align-items:center; justify-content:space-between; gap:12px; width:100%; }
.bld-back { appearance:none; border:0; background:transparent; cursor:pointer; font-family:var(--f-sans); font-size:.92rem; font-weight:600; color:var(--soft); padding:6px 2px; border-radius:8px; transition:color .15s; }
.bld-back:hover { color:var(--accent-text); }
.bld-head-title { margin-top:10px; }
.bld-eyebrow { font-family:var(--f-mono); font-size:.66rem; letter-spacing:.16em; text-transform:uppercase; color:var(--faint); }
.bld-head-name { font-size:1.18rem; margin-top:3px; word-break:break-word; }
.bld-lang-seg button { padding:5px 11px; }
/* Icon-only light/dark control — same sun/moon as the visitor-facing toggle
   (see THEME_ICON in scripts/builder.js). Square so the two sit as a pair. */
.bld-theme-seg-icon button { padding:5px 9px; display:grid; place-items:center; }
.bld-theme-seg-icon svg { width:17px; height:17px; display:block; }

/* Language switcher inside the panel head. The panel covers the top bar, so
   this is the only way to change language while Settings is open — see the
   note in includes/footer.php. Pushed right so the title stays left-aligned
   and the close button stays in its usual corner. */
.settings-panel .sp-head .sp-lang { margin-left:auto; margin-right:10px; }
.settings-panel .sp-head .sp-lang a { padding:5px 11px; font-size:.78rem; }

/* Builder header stacks vertically: back + control on row 1, title below.
   The builder replaces this header's contents wholesale, so the switcher
   above is only present on the Settings HOME screen — which is where the
   Language block itself lives, so it's in the right place either way. */
.settings-panel[data-builder-active="true"] .sp-head { display:block; }

/* In-form layout toggle (Stacked / Tabbed). */
.bld-layout-row { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:18px; padding-bottom:16px; border-bottom:1px solid var(--hair); }
.bld-layout-label { font-family:var(--f-mono); font-size:.66rem; letter-spacing:.16em; text-transform:uppercase; color:var(--faint); }
.bld-layout-seg button { padding:5px 13px; }

/* ---- Form scaffold ------------------------------------------------------- */
.bld-form { display:grid; gap:26px; }
.bld-section { display:grid; gap:14px; padding-bottom:22px; border-bottom:1px solid var(--hair); }
.bld-form .bld-section:last-child { border-bottom:0; padding-bottom:0; }
.bld-section-title { font-size:.78rem; letter-spacing:.12em; text-transform:uppercase; color:var(--accent-text); font-family:var(--f-sans); font-weight:700; }

.bld-field { display:grid; gap:7px; }
.bld-label { font-size:.86rem; font-weight:600; color:var(--ink-2); display:flex; align-items:center; gap:8px; }
.bld-lang-tag { font-family:var(--f-mono); font-size:.6rem; letter-spacing:.1em; color:var(--accent-text); background:var(--accent-soft); border-radius:5px; padding:2px 5px; }
.bld-hint { font-size:.78rem; color:var(--soft); margin:0; line-height:1.45; }
/* A hint sitting directly under its own label, with no gap above. Used where
   the guidance belongs to the field above it rather than the control below. */
.bld-hint-tight { margin-top:-2px; }
/* A hint that is a caveat, not a nicety (e.g. "this host can't build zips, so
   your images are NOT in the download") — same size, warning color + weight. */
.bld-hint.bld-warn { color:var(--warn); font-weight:600; }

.bld-input { width:100%; font-family:var(--f-sans); font-size:.92rem; color:var(--ink); background:var(--bg); border:1px solid var(--line); border-radius:10px; padding:10px 12px; transition:border-color .15s, box-shadow .15s; }
.bld-input:focus { outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft); }
.bld-textarea { resize:vertical; line-height:1.5; min-height:84px; }
.bld-select { appearance:none; cursor:pointer; background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2362636F' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); background-repeat:no-repeat; background-position:right 12px center; padding-inline-end:34px; }
/* background-position has no logical "inline-end" value, so RTL needs an
   explicit override to move the chevron to the other side. */
[dir="rtl"] .bld-select { background-position:left 12px center; }
.bld-mono { font-family:var(--f-mono); font-size:.84rem; }
.bld-input.is-locked { color:var(--soft); background:var(--hair); cursor:not-allowed; }
/* Rich-text field: formatting toolbar over a contenteditable editor. */
.bld-rt { display:grid; gap:0; }
.bld-rt-bar { display:flex; flex-wrap:wrap; gap:4px; padding:6px; background:var(--hair); border:1px solid var(--line); border-bottom:0; border-radius:10px 10px 0 0; }
.bld-rt-btn { min-width:30px; height:30px; display:inline-flex; align-items:center; justify-content:center; padding:0 8px; font-family:var(--f-sans); font-size:.9rem; line-height:1; color:var(--ink-2); background:var(--surface); border:1px solid var(--line); border-radius:7px; cursor:pointer; transition:background .12s, color .12s, border-color .12s; }
.bld-rt-btn:hover { color:var(--accent-text); border-color:var(--accent); }
.bld-rt-btn[aria-pressed="true"] { color:var(--on-accent); background:var(--accent); border-color:var(--accent); }
.bld-rt-editor { min-height:96px; line-height:1.5; border-radius:0 0 10px 10px; }
.bld-rt-editor:focus { outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft); }
.bld-rt-editor p { margin:0 0 .6em; }
.bld-rt-editor p:last-child { margin-bottom:0; }
.bld-rt-editor ul, .bld-rt-editor ol { margin:0 0 .6em; padding-inline-start:1.4em; }
.bld-rt-editor blockquote { margin:0 0 .6em 1.4em; }
.bld-rt-editor a { color:var(--accent-text); }
/* On touch devices, iOS Safari zooms the whole viewport when a control with a
   font-size under 16px is focused — which visually enlarged the fixed Settings
   panel and its Save/Discard footer. Bumping panel controls to 16px on coarse
   pointers stops the zoom (and the footer no longer scales). Desktop keeps the
   more compact .92rem sizing. */
@media (pointer: coarse) {
  .settings-panel input,
  .settings-panel select,
  .settings-panel textarea,
  .settings-panel .bld-input { font-size:16px; }
}

/* ---- List editor --------------------------------------------------------- */
.bld-list { display:grid; gap:8px; }
.bld-list-items { display:grid; gap:8px; }
.bld-list-row, .bld-color-row { display:flex; align-items:center; gap:8px; }
.bld-list-row .bld-input, .bld-color-hex { flex:1; }
.bld-empty { font-size:.82rem; color:var(--faint); margin:0; font-style:italic; }
.bld-icon-btn { appearance:none; flex:0 0 auto; width:34px; height:34px; border-radius:9px; border:1px solid var(--line); background:var(--surface); color:var(--soft); font-size:1.2rem; line-height:1; cursor:pointer; display:grid; place-items:center; transition:color .15s, border-color .15s, background .15s; }
.bld-icon-btn:hover { color:var(--warn); border-color:var(--warn); }
.bld-add { appearance:none; cursor:pointer; font-family:var(--f-sans); font-size:.84rem; font-weight:600; color:var(--accent-text); background:var(--accent-soft); border:0; border-radius:9px; padding:8px 14px; transition:filter .15s; }
.bld-add:hover { filter:brightness(.97); }

/* ---- Color editor -------------------------------------------------------- */
.bld-color-dot { flex:0 0 auto; width:38px; height:34px; padding:0; border:1px solid var(--line); border-radius:9px; background:none; cursor:pointer; }
.bld-color-dot::-webkit-color-swatch-wrapper { padding:3px; }
.bld-color-dot::-webkit-color-swatch { border:0; border-radius:6px; }

/* ---- Image field --------------------------------------------------------- */
.bld-image { display:grid; gap:8px; }
.bld-image-prev { width:100%; aspect-ratio:var(--ar-video); border:1px solid var(--line); border-radius:10px; overflow:hidden; background:repeating-linear-gradient(45deg,var(--hair),var(--hair) 9px,var(--bg) 9px,var(--bg) 18px); display:grid; place-items:center; }
.bld-image-prev img { width:100%; height:100%; object-fit:cover; }
/* A headshot is not 16:9. This preview keeps whatever shape the photo is, in a
   box tall enough to judge it by, instead of letterboxing a portrait. */
.bld-image-prev--free { aspect-ratio:auto; min-height:180px; max-height:280px; padding:8px; }
.bld-image-prev--free img { width:auto; max-width:100%; height:100%; object-fit:contain; }
.bld-image-controls { display:flex; flex-wrap:wrap; gap:8px; align-items:center; }
.bld-image-controls .bld-input { flex:1; min-width:160px; }
.bld-upload-btn { flex:0 0 auto; }
.bld-ph { font-family:var(--f-mono); font-size:.7rem; letter-spacing:.14em; color:var(--faint); display:grid; gap:6px; justify-items:center; text-align:center; padding:0 12px; }
.bld-ph-drop { font-family:var(--f-sans); letter-spacing:0; font-size:.78rem; color:var(--soft); }

/* Drop targets. The dashed outline only appears mid-drag, so the resting
   state of the panel is unchanged — and `pointer-events:none` on the contents
   keeps the drag events on the zone itself rather than on whatever child
   happens to be under the cursor. */
.bld-image-prev { transition:border-color .16s, background-color .16s; }
.bld-image-prev.is-dragover,
.carousel-wrap.is-dragover { border-color:var(--accent); background-color:var(--accent-soft); outline:2px dashed var(--accent); outline-offset:-6px; }
.bld-image-prev.is-dragover > *, .carousel-wrap.is-dragover > * { pointer-events:none; }
.carousel-wrap.is-dragover { border-radius:var(--radius); }

/* ---- Visibility toggles + status badge ----------------------------------- */
.bld-visibility { display:grid; gap:16px; }
.bld-badge-row { display:flex; }
.bld-badge { font-size:.72rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase; padding:5px 11px; border-radius:999px; }
.bld-badge.is-live { color:var(--ok); background:color-mix(in srgb, var(--ok) 14%, transparent); }
.bld-badge.is-soon { color:var(--warn); background:color-mix(in srgb, var(--warn) 14%, transparent); }
.bld-badge.is-draft { color:var(--soft); background:var(--hair); }
.bld-badge.is-revision { color:var(--accent-text); background:var(--accent-soft); }
.bld-badge.is-archiving { color:#c0392b; background:color-mix(in srgb, #c0392b 14%, transparent); }
.bld-badge.is-archived { color:var(--soft); background:var(--hair); }
.bld-badge.is-restoring { color:var(--ok); background:color-mix(in srgb, var(--ok) 14%, transparent); }
.bld-revision-banner { display:grid; gap:10px; padding:14px 16px; border-radius:12px; margin-bottom:16px; background:var(--accent-soft); border:1px solid color-mix(in srgb, var(--accent) 35%, var(--line)); }
.bld-revision-text { margin:0; font-size:.86rem; color:var(--accent-ink); line-height:1.45; }
.bld-revision-actions { display:flex; gap:10px; flex-wrap:wrap; }
.bld-toggle-row { display:flex; align-items:flex-start; justify-content:space-between; gap:14px; }
.bld-toggle-text { display:grid; gap:3px; }
.bld-toggle-label { font-size:.9rem; font-weight:600; color:var(--ink-2); }
.bld-toggle-hint { font-size:.78rem; color:var(--soft); line-height:1.45; }
.bld-switch { flex:0 0 auto; position:relative; cursor:pointer; }
.bld-switch input { position:absolute; opacity:0; width:0; height:0; }
.bld-track { display:block; width:44px; height:26px; border-radius:999px; background:var(--line); transition:background .2s; position:relative; }
.bld-thumb { position:absolute; top:3px; inset-inline-start:3px; width:20px; height:20px; border-radius:50%; background:#fff; box-shadow:0 1px 3px rgba(0,0,0,.3); transition:transform .2s; }
.bld-switch input:checked + .bld-track { background:var(--accent); }
.bld-switch input:checked + .bld-track .bld-thumb { transform:translateX(18px); }
[dir="rtl"] .bld-switch input:checked + .bld-track .bld-thumb { transform:translateX(-18px); }
.bld-switch input:focus-visible + .bld-track { box-shadow:0 0 0 3px var(--accent-soft); }

/* ---- Tabs layout --------------------------------------------------------- */
.bld-form .bld-rail { display:flex; flex-wrap:wrap; gap:6px; margin-bottom:6px; }
.bld-tab { appearance:none; cursor:pointer; font-family:var(--f-sans); font-size:.8rem; font-weight:600; color:var(--soft); background:var(--bg); border:1px solid var(--line); border-radius:999px; padding:7px 13px; transition:all .15s; }
.bld-tab:hover { color:var(--ink-2); border-color:var(--soft); }
.bld-tab[aria-selected="true"] { color:var(--on-accent); background:var(--accent); border-color:var(--accent); }
.bld-pane { display:grid; gap:14px; transition:opacity .13s ease; }
.bld-pane.bld-fade-out { opacity:0; }

/* ---- Storyboard grid ----------------------------------------------------- */
.bld-table { display:grid; gap:10px; }
.bld-table-bar { display:flex; flex-wrap:wrap; gap:6px; }
.bld-table-btn { appearance:none; cursor:pointer; font-family:var(--f-sans); font-size:.76rem; font-weight:600; color:var(--ink-2); background:var(--surface); border:1px solid var(--line); border-radius:8px; padding:6px 10px; transition:border-color .15s, color .15s; }
.bld-table-btn:hover { color:var(--accent-text); border-color:var(--accent); }
/* ---- Backup & Restore list rows ---- */
.bld-backup-list { display:grid; gap:8px; }
.bld-backup-row { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:10px 12px; border:1px solid var(--line); border-radius:10px; background:var(--bg); }
.bld-backup-meta { display:flex; flex-direction:column; gap:2px; min-width:0; }
.bld-backup-time { font-size:.88rem; font-weight:600; color:var(--ink); }
.bld-backup-sub { font-size:.76rem; color:var(--soft); }
.bld-backup-actions { display:flex; gap:6px; flex:none; }
.bld-backup-actions .bld-table-btn { text-decoration:none; }
/* overflow-y:auto + max-height — same fix, same reason, as .tbl-scroll above:
   a sticky header only sticks correctly relative to a box that's actually
   scrolling, and overflow-x:auto alone silently makes this box that
   reference frame anyway (the spec computes overflow-y to auto once
   overflow-x isn't visible) without giving it real bounded scroll to stick
   within. */
.bld-table-scroll { overflow-x:auto; overflow-y:auto; max-height:65vh; border:1px solid var(--line); border-radius:10px; }
.bld-grid { border-collapse:collapse; width:100%; min-width:480px; }
.bld-grid th, .bld-grid td { border:1px solid var(--line); padding:0; vertical-align:top; }
.bld-grid th { background:var(--hair); }
/* Contenteditable storyboard cells (merge-capable editor). */
.bld-grid td.bld-gcell, .bld-grid th.bld-gcell { min-width:96px; padding:8px 9px; font-family:var(--f-sans); font-size:.82rem; color:var(--ink); cursor:text; outline:none; }
.bld-grid th.bld-gcell { font-weight:700; }
.bld-gcell:focus { box-shadow:inset 0 0 0 2px var(--accent); }
.bld-grid .is-sel { background:var(--accent-soft); }

/* ---- Implement / Evaluate: repeatable records ---------------------------- */
/* One card per rollout, per round of measurement. Drawn as a bounded card so
   two rollouts of the same resource never blur into one another. */
.bld-recs-wrap { display:grid; gap:12px; }
.bld-recs { display:grid; gap:14px; }
.bld-rec { display:grid; gap:14px; padding:14px; border:1px solid var(--line); border-radius:12px; background:var(--bg); }
.bld-rec-head { display:flex; align-items:center; justify-content:space-between; gap:10px; }
/* The record name takes the treatment the group title used to have, now that
   the phase header above it already says which phase this is. */
.bld-rec-name { font-family:var(--f-sans); font-size:.78rem; font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:var(--accent-text); }
.bld-rec-sub { font-family:var(--f-sans); font-size:.7rem; font-weight:700; letter-spacing:.14em; text-transform:uppercase; color:var(--label-ink); margin:6px 0 -4px; }
/* Spans the panel and sits last, so it reads as acting on the whole phase
   rather than on the field above it. */
.bld-add-rec { justify-self:stretch; width:100%; text-align:center; padding:11px 14px; border:1px dashed color-mix(in srgb, var(--accent) 40%, var(--line)); background:transparent; }
.bld-add-rec:hover { background:var(--accent-soft); filter:none; }

.bld-daterange { display:flex; gap:10px; flex-wrap:wrap; }
.bld-daterange-part { flex:1 1 150px; display:grid; gap:4px; }
.bld-daterange-label { font-size:.72rem; color:var(--soft); }
.bld-methodpick { display:grid; gap:8px; }

/* ---- Versioned document slot --------------------------------------------- */
.bld-doc { display:grid; gap:10px; padding:12px; border:1px solid var(--line); border-radius:11px; background:var(--surface); }
.bld-doc-current { display:grid; gap:9px; }
.bld-doc-row { display:flex; align-items:center; gap:9px; flex-wrap:wrap; }
.bld-doc-badge { font-size:.62rem; font-weight:700; letter-spacing:.1em; text-transform:uppercase; color:var(--accent-text); background:var(--accent-soft); border-radius:5px; padding:3px 7px; }
.bld-doc-v { font-family:var(--f-mono); font-size:.74rem; font-weight:700; color:var(--ink-2); }
.bld-doc-file { font-family:var(--f-mono); font-size:.76rem; color:var(--soft); overflow-wrap:anywhere; min-width:0; }
.bld-doc-meta { font-size:.74rem; color:var(--faint); }
.bld-doc-note { display:grid; gap:4px; }
.bld-doc-note-label { font-size:.72rem; color:var(--soft); }
.bld-doc-actions { display:flex; gap:8px; flex-wrap:wrap; }
.bld-doc-hist > summary { cursor:pointer; font-size:.78rem; color:var(--accent-text); font-weight:600; }
.bld-doc-old { display:grid; gap:7px; margin-top:9px; }
.bld-doc-oldrow { display:flex; align-items:center; gap:9px; flex-wrap:wrap; padding:8px 10px; border:1px solid var(--line); border-radius:9px; background:var(--bg); }

/* ---- Issues by priority --------------------------------------------------- */
/* Colour is a SECOND channel here, never the only one: the priority is named in
   the cell beside it, chosen from a dropdown, and stored as a value rather than
   read back from the typed word. Ink is pinned per theme because these tints
   stop being backgrounds you can put --ink on. */
.bld-sevgrid td.bld-gcell { vertical-align:middle; }
.bld-sevgrid tr.bld-sev td { background:var(--sev-bg); color:var(--sev-ink); }
tr.sev-critical { --sev-bg:#FDE7E4; --sev-ink:#16161F; }
tr.sev-major    { --sev-bg:#FDF3D6; --sev-ink:#16161F; }
tr.sev-minor    { --sev-bg:#E6F0FC; --sev-ink:#16161F; }
tr.sev-normal   { --sev-bg:transparent; --sev-ink:var(--ink); }
html[data-theme="dark"] tr.sev-critical { --sev-bg:#4A2320; --sev-ink:#F4E9E7; }
html[data-theme="dark"] tr.sev-major    { --sev-bg:#46391A; --sev-ink:#F5EFE2; }
html[data-theme="dark"] tr.sev-minor    { --sev-bg:#1E3350; --sev-ink:#E5EDF7; }
.bld-dv td.bld-gcell { vertical-align:middle; }
.bld-dv .bld-input, .bld-sevgrid .bld-input { font-size:.84rem; padding:6px 10px; }

/* ---- Multi-select chips + per-measure figures ---------------------------- */
.bld-chips { display:flex; flex-wrap:wrap; gap:7px; }
.bld-chip { display:inline-flex; align-items:center; gap:6px; font-size:.82rem; font-weight:600; color:var(--ink-2); background:var(--surface); border:1px solid var(--line); border-radius:999px; padding:6px 12px; cursor:pointer; }
.bld-chip:has(input:checked) { color:var(--accent-ink); background:var(--accent-soft); border-color:color-mix(in srgb, var(--accent) 35%, var(--line)); }
.bld-chip.is-own { color:var(--accent-ink); background:var(--accent-soft); border-color:color-mix(in srgb, var(--accent) 35%, var(--line)); }
.bld-chip-add { border-style:dashed; color:var(--accent-text); background:transparent; }
.bld-chip-x { appearance:none; border:0; background:none; cursor:pointer; color:inherit; font-size:1rem; line-height:1; padding:0 0 0 2px; }
.bld-quant { display:grid; gap:10px; }
.bld-quant-vals { display:grid; gap:8px; }
.bld-quant-row { display:flex; align-items:center; gap:10px; }
.bld-quant-name { flex:1 1 auto; min-width:0; font-size:.84rem; font-weight:600; color:var(--ink-2); }
.bld-quant-row .bld-input { flex:0 0 132px; }

.bld-metwrap { display:grid; gap:10px; }
.bld-choice { display:flex; flex-wrap:wrap; gap:7px; }
.bld-choice-opt { display:inline-flex; align-items:center; gap:6px; font-size:.84rem; font-weight:600; color:var(--ink-2); background:var(--surface); border:1px solid var(--line); border-radius:999px; padding:6px 13px; cursor:pointer; }
.bld-choice-opt:has(input:checked) { color:var(--accent-ink); background:var(--accent-soft); border-color:color-mix(in srgb, var(--accent) 35%, var(--line)); }

/* ---- Sticky save footer -------------------------------------------------- */
.bld-footer { display:none; }
.settings-panel[data-builder-active="true"] .bld-footer { display:flex; align-items:center; flex:none; gap:10px; padding:16px 22px; border-top:1px solid var(--line); background:color-mix(in srgb, var(--surface) 92%, transparent); backdrop-filter:blur(8px); }
/* Primary save stays on ONE line (never wraps) even when cramped on mobile. */
/* The save-time embed warning sits on its own line UNDER the buttons, so the
   footer has to be allowed to wrap for it. flex-basis:100% is what pushes it
   onto that second line rather than squeezing in beside the buttons. */
.settings-panel[data-builder-active="true"] .bld-footer { flex-wrap:wrap; }
/* Builder showcase demo only (window.DEMO_MODE) — see ensureSaveFooter() in
   scripts/builder.js. Pushed left of the buttons via margin-inline-end:auto. */
.bld-demo-notice { font-size:.78rem; color:var(--soft); margin-inline-end:auto; }
.bld-embed-confirm { flex:1 0 100%; margin-top:4px; padding:13px 15px;
  border:1.5px solid var(--accent); border-radius:12px; background:var(--accent-soft); }
.bld-embed-confirm .bec-msg { margin:0 0 8px; font-size:.86rem; line-height:1.45; color:var(--ink-2); }
.bld-embed-confirm .bec-url { display:block; margin-bottom:11px; font-family:var(--f-mono);
  font-size:.74rem; color:var(--soft); overflow-wrap:anywhere; }
.bld-embed-confirm .bec-row { display:flex; gap:8px; flex-wrap:wrap; }
.bld-embed-confirm .bec-row button { flex:1 1 auto; }

.bld-save { flex:1; appearance:none; cursor:pointer; font-family:var(--f-sans); font-size:.95rem; font-weight:700; white-space:nowrap; color:var(--on-accent); background:var(--accent); border:0; border-radius:11px; padding:13px; transition:background .15s; }
.bld-save:hover { background:var(--accent-2); }
.bld-save:disabled { opacity:.6; cursor:default; }
.bld-save-secondary { flex:0 0 auto; appearance:none; cursor:pointer; font-family:var(--f-sans); font-size:.9rem; font-weight:600; color:var(--ink-2); background:transparent; border:1.5px solid var(--line); border-radius:11px; padding:13px 16px; transition:border-color .15s, color .15s; }
.bld-save-secondary:hover { color:var(--accent-text); border-color:var(--accent); }
/* Discard changes: bordered so it clearly reads as a button (still quieter than
   Save via the muted color). It may wrap to two lines when the footer is tight
   on mobile, but the reduced padding keeps its tap area from growing taller than
   the single-line Save/Draft buttons beside it. */
.bld-discard { flex:0 1 auto; min-width:0; appearance:none; cursor:pointer; font-family:var(--f-sans); font-size:.9rem; font-weight:600; line-height:1.15; text-align:center; white-space:normal; color:var(--soft); background:transparent; border:1.5px solid var(--line); border-radius:11px; padding:8px 12px; transition:color .15s, border-color .15s; }
.bld-discard:hover { color:var(--warn); border-color:var(--warn); }
/* Transient confirmation pill (e.g. "Changes discarded"), pinned just above the
   footer inside the Settings panel; fades in/out via .is-in. */
.bld-toast { position:absolute; left:50%; bottom:88px; transform:translate(-50%, 8px); z-index:5; max-width:calc(100% - 44px); padding:10px 16px; border-radius:999px; background:var(--ink); color:var(--surface); font-family:var(--f-sans); font-size:.85rem; font-weight:600; box-shadow:0 8px 24px -10px rgba(0,0,0,.5); opacity:0; pointer-events:none; transition:opacity .2s ease, transform .2s ease; }
.bld-toast.is-in { opacity:.96; transform:translate(-50%, 0); }

/* ---- Save stub (JSON preview) -------------------------------------------- */
.bld-stub-card { display:grid; gap:12px; margin-top:14px; }
.bld-json { font-family:var(--f-mono); font-size:.74rem; line-height:1.5; color:var(--ink-2); background:var(--bg); border:1px solid var(--line); border-radius:10px; padding:14px; overflow:auto; max-height:340px; white-space:pre; }

@media (max-width:680px) {
  .settings-panel[data-builder-active="true"] { width:100%; max-width:100vw; }
}

/* ---- Header controls: EN/ES + Live-preview toggle ------------------------ */
.bld-head-controls { display:flex; align-items:center; gap:10px; }
.bld-preview-toggle { appearance:none; cursor:pointer; font-family:var(--f-sans); font-size:.82rem; font-weight:600; color:var(--soft); background:var(--bg); border:1.5px solid var(--line); border-radius:9px; padding:6px 12px; transition:color .15s, border-color .15s, background .15s; white-space:nowrap; }
.bld-preview-toggle:hover { color:var(--accent-text); border-color:var(--accent); }
.bld-preview-toggle[aria-pressed="true"] { color:var(--on-accent); background:var(--accent); border-color:var(--accent); }

/* ---- Phase headers in the stacked editor --------------------------------- */
.bld-phase-head { display:flex; align-items:center; gap:12px; margin:14px 0 -6px; }
.bld-phase-head:first-child { margin-top:0; }
.bld-phase-head::after { content:""; flex:1; height:1px; background:var(--line); }
.bld-phase-name { flex:none; font-family:var(--f-mono); font-size:.7rem; font-weight:700; letter-spacing:.16em; text-transform:uppercase; color:var(--accent-ink); }

/* ---- Live-preview drawer (opens to the LEFT of the settings panel) -------- */
.bld-preview-panel { position:fixed; top:0; bottom:0; right:640px; width:clamp(340px, 40vw, 640px); z-index:92; background:var(--bg); border-left:1px solid var(--line); box-shadow:-26px 0 60px -34px rgba(0,0,0,.45); display:flex; flex-direction:column; transform:translateX(24px); opacity:0; pointer-events:none; transition:opacity .28s cubic-bezier(.22,.61,.36,1), transform .28s cubic-bezier(.22,.61,.36,1); }
.bld-preview-panel.open { transform:none; opacity:1; pointer-events:auto; }
.bld-preview-head { flex:none; display:flex; align-items:center; justify-content:space-between; gap:12px; padding:14px 18px; border-bottom:1px solid var(--line); background:var(--surface); }
.bld-preview-title { font-family:var(--f-mono); font-size:.68rem; font-weight:700; letter-spacing:.14em; text-transform:uppercase; color:var(--faint); }
.bld-preview-actions { display:flex; align-items:center; gap:8px; }
.bld-preview-btn { appearance:none; cursor:pointer; font-family:var(--f-sans); font-size:.82rem; font-weight:600; color:var(--ink-2); background:var(--bg); border:1.5px solid var(--line); border-radius:9px; padding:6px 12px; transition:color .15s, border-color .15s; }
.bld-preview-btn:hover { color:var(--accent-text); border-color:var(--accent); }
.bld-preview-close { appearance:none; cursor:pointer; width:30px; height:30px; border-radius:8px; border:0; background:transparent; color:var(--soft); font-size:1.4rem; line-height:1; transition:color .15s, background .15s; }
.bld-preview-close:hover { color:var(--ink); background:var(--hair); }
.bld-preview-frame { position:relative; flex:1 1 auto; min-height:0; }
/* Two stacked buffers for double-buffered updates (a reload never flashes the
   preview back to the top). Both stay RENDERED and opaque; the inactive one is
   simply covered by the active one via z-index — a covered iframe can still be
   scrolled into position before it's brought to the front, whereas a
   visibility:hidden iframe cannot (scroll gets deferred until it's shown). */
.bld-preview-iframe { position:absolute; inset:0; width:100%; height:100%; border:0; display:block; background:#fff; z-index:1; }
.bld-preview-iframe.is-active { z-index:2; }
.bld-preview-panel[data-empty="true"] .bld-preview-iframe { display:none; }
.bld-preview-empty { display:none; }
.bld-preview-panel[data-empty="true"] .bld-preview-empty { display:grid; place-items:center; position:absolute; inset:0; padding:40px; text-align:center; color:var(--soft); font-size:.95rem; }
@media (prefers-reduced-motion:reduce) { .bld-preview-panel { transition:none; } }
/* Not enough room for a side-by-side preview — hide the drawer + its toggle. */
@media (max-width:1180px) {
  .bld-preview-panel { display:none; }
  .bld-preview-toggle { display:none; }
}

/* ===========================================================================
   SETTINGS HOME entry blocks + the two new editor screens
   =========================================================================== */
/* Home: each setting is a titled block with a hint and an action. */
.sp-body > .bld-home-block { display:grid; gap:10px; padding-bottom:20px; margin-bottom:20px; border-bottom:1px solid var(--hair); }
.sp-body > .bld-home-block:last-child { border-bottom:0; margin-bottom:0; padding-bottom:0; }
.bld-home-block > h3 { font-size:.98rem; }
.bld-open { justify-self:start; }

/* Language toggle: the second-language select + default-language select only
   make sense once a second language is on, so they're hidden until the
   checkbox is checked — no JS needed for this, :has() does it. */
.bld-lang-block .bld-lang-fields { display:none; }
.bld-lang-block:has(#bld-lang-enabled:checked) .bld-lang-fields { display:grid; gap:14px; margin-top:2px; }

/* ---- Work categories editor --------------------------------------------- */
.bld-cat-list { display:grid; gap:10px; }
.bld-cat-row { display:grid; grid-template-columns:22px 1fr auto; align-items:start; gap:12px; padding:12px; border:1px solid var(--line); border-radius:12px; background:var(--bg); }
.bld-cat-row.dragging { opacity:.55; border-color:var(--accent); box-shadow:0 8px 22px -10px rgba(0,0,0,.35); }
.bld-drag { cursor:grab; color:var(--faint); font-size:1rem; line-height:1.9; user-select:none; text-align:center; }
.bld-drag:active { cursor:grabbing; }
.bld-cat-fields { display:grid; gap:8px; min-width:0; }
.bld-cat-names { display:grid; grid-template-columns:1fr 1fr; gap:8px; }
/* Only one name field when the second language is off (see catRow() in
   scripts/builder.js) — let it use the full width instead of half. */
.bld-cat-names.is-single { grid-template-columns:1fr; }
.bld-cat-meta { display:flex; flex-direction:column; align-items:flex-end; gap:8px; }
.bld-cat-count { font-family:var(--f-mono); font-size:.68rem; letter-spacing:.04em; color:var(--soft); white-space:nowrap; }
.bld-cat-warn { grid-column:1 / -1; margin:2px 0 0; font-size:.78rem; color:var(--warn); }
.bld-cat-move { grid-column:1 / -1; display:grid; gap:8px; margin-top:4px; padding-top:12px; border-top:1px dashed var(--line); }

/* ---- Arrange resources editor (drag to reorder / move between / archive) - */
.bld-arr-groups { display:grid; gap:22px; }
.bld-arr-group { display:grid; gap:10px; }
.bld-arr-head { display:flex; align-items:baseline; justify-content:space-between; gap:10px; }
.bld-arr-list { display:grid; gap:8px; min-height:48px; padding:6px; border:1px dashed var(--line); border-radius:12px; transition:border-color .15s, background .15s; }
.bld-arr-list.over { border-color:var(--accent); border-style:solid; background:var(--accent-soft); }
.bld-arr-empty { margin:0; padding:11px 8px; font-size:.82rem; color:var(--faint); text-align:center; }
.bld-arr-item { display:grid; grid-template-columns:22px 42px 1fr; align-items:center; gap:12px; padding:9px 11px; border:1px solid var(--line); border-radius:10px; background:var(--surface); cursor:grab; }
.bld-arr-item:active { cursor:grabbing; }
.bld-arr-item.dragging { opacity:.55; border-color:var(--accent); box-shadow:0 8px 22px -10px rgba(0,0,0,.35); }
.bld-arr-thumb { width:42px; height:26px; border-radius:5px; overflow:hidden; background:var(--hair); display:block; }
.bld-arr-thumb img { width:100%; height:100%; object-fit:cover; display:block; }
.bld-arr-text { display:flex; align-items:center; justify-content:space-between; gap:10px; min-width:0; }
.bld-arr-title { font-weight:600; font-size:.92rem; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.bld-arr-text .bld-badge { flex:0 0 auto; font-size:.64rem; padding:4px 9px; }

/* Archived items: a divider, then a muted drop zone you can drag back out of. */
.bld-arr-divider { border:0; border-top:1px solid var(--line); margin:26px 0 4px; }
.bld-arr-archived .bld-section-title { color:var(--soft); }
.bld-arr-archived .bld-arr-list { background:color-mix(in srgb, var(--hair) 60%, transparent); }
.bld-arr-archived .bld-arr-item { opacity:.72; }
.bld-arr-archived .bld-arr-item:hover { opacity:1; }
.bld-arr-archived .bld-hint { margin:-2px 0 10px; }
.bld-cat-move-row { display:flex; flex-wrap:wrap; gap:8px; align-items:center; }
.bld-cat-move-row .bld-select { flex:1; min-width:140px; }

/* ---- Danger zone: archive an existing resource --------------------------- */
.bld-danger { border-top:1px solid var(--hair); margin-top:8px; padding-top:18px; }
.bld-danger .bld-section-title { color:color-mix(in srgb, #c0392b 70%, var(--ink)); }
html[data-theme="dark"] .bld-danger .bld-section-title { color:#e8897f; }
.bld-danger-slot { margin-top:12px; }
.bld-archive-btn { appearance:none; cursor:pointer; font-family:var(--f-sans); font-weight:600; font-size:.86rem; padding:10px 16px; border-radius:9px; background:transparent; color:#c0392b; border:1.5px solid color-mix(in srgb,#c0392b 45%,var(--line)); }
.bld-archive-btn:hover { background:color-mix(in srgb,#c0392b 10%,transparent); border-color:#c0392b; }
.bld-archive-btn.is-confirm { background:#c0392b; color:#fff; border-color:#c0392b; }
.bld-archive-btn.is-confirm:hover { background:#a93226; }
.bld-danger-q { margin:0 0 10px; font-size:.9rem; color:var(--ink-2); font-weight:600; }
.bld-danger-actions { display:flex; gap:10px; }
/* ---- Delete: same danger-zone treatment, but the button is solid by default
   (not just on confirm) to read as more severe/final than Archive. ---------- */
.bld-danger-delete { border-top:1px dashed color-mix(in srgb,#c0392b 35%,var(--hair)); margin-top:18px; padding-top:18px; }
.bld-delete-btn { background:color-mix(in srgb,#c0392b 8%,transparent); }
.bld-delete-btn:hover { background:color-mix(in srgb,#c0392b 16%,transparent); }
.bld-archive-cancel { appearance:none; cursor:pointer; font-family:var(--f-sans); font-weight:600; font-size:.86rem; padding:10px 16px; border-radius:9px; background:transparent; color:var(--soft); border:1.5px solid var(--line); }
.bld-archive-cancel:hover { color:var(--ink); border-color:var(--ink-2); }

/* ---- Site colors editor -------------------------------------------------- */
.bld-theme-seg button { padding:5px 13px; }
.bld-sc-reset { margin-top:4px; }

/* ---- Visual Style: labeled color/font rows bound to a site role ---------- */
.bld-vs-list { display:grid; gap:10px; }
/* 8px gap between the color/font list and its "Add" button. */
.bld-vs-add { margin-top:8px; }
.bld-vs-preview { padding:16px; margin-bottom:14px; background:var(--bg); border:1px dashed var(--line); border-radius:12px; }
.bld-vs-preview .bld-empty { margin:0; }
.bld-vs-row { display:grid; grid-template-columns:1fr auto; grid-template-rows:auto auto; gap:8px 12px; align-items:center; background:var(--bg); border:1px solid var(--line); border-radius:12px; padding:12px; }
.bld-vs-row-main { grid-column:1; grid-row:1; display:flex; gap:8px; align-items:center; position:relative; }
.bld-vs-row-main .bld-vs-label { flex:1 1 auto; min-width:0; }
.bld-vs-row-main .bld-vs-role { flex:0 0 150px; width:150px; min-width:0; text-overflow:ellipsis; overflow:hidden; }
/* Custom label-picker popover (replaces native <datalist>, which filters its
   own suggestions to the input's current text — useless once a label is
   filled in). Anchored to .bld-vs-row-main via that element's position:relative. */
.bld-label-menu { position:absolute; top:calc(100% + 4px); left:0; right:0; z-index:20; max-height:220px; overflow-y:auto; background:var(--surface); border:1px solid var(--line); border-radius:10px; box-shadow:0 14px 34px rgba(22,22,31,0.18); padding:6px; display:grid; gap:2px; }
.bld-label-menu-row { display:flex; align-items:center; gap:4px; }
.bld-label-menu-pick { flex:1; text-align:start; appearance:none; cursor:pointer; background:none; border:0; border-radius:7px; padding:8px 10px; font-family:var(--f-sans); font-size:.86rem; color:var(--ink); }
.bld-label-menu-pick:hover { background:var(--bg); color:var(--accent-text); }
.bld-label-menu-del { flex:none; appearance:none; cursor:pointer; width:26px; height:26px; border-radius:7px; border:0; background:none; color:var(--faint); font-size:1.05rem; line-height:1; }
.bld-label-menu-del:hover { color:var(--warn); background:var(--bg); }
.bld-vs-row > .bld-color-row { grid-column:1; grid-row:2; }
/* Contrast checker badge: color is never the only signal — an icon (check/
   cross) and the numeric ratio are always shown too, for colorblind users. */
.bld-contrast-badge { flex:0 0 auto; display:flex; align-items:center; gap:4px; padding:4px 9px; border-radius:999px; font-size:.72rem; font-weight:700; font-family:var(--f-mono); white-space:nowrap; }
.bld-contrast-badge.is-pass { color:var(--ok); background:color-mix(in srgb, var(--ok) 16%, transparent); }
.bld-contrast-badge.is-fail { color:var(--warn); background:color-mix(in srgb, var(--warn) 16%, transparent); }
.bld-contrast-icon { font-size:.9rem; line-height:1; }
.bld-vs-row > .bld-icon-btn { grid-column:2; grid-row:1 / span 2; align-self:center; }
.bld-vs-row > .bld-vs-actions { grid-column:2; grid-row:1 / span 2; align-self:center; display:flex; align-items:center; gap:6px; }
.bld-vs-move { display:flex; flex-direction:column; gap:4px; }

/* Carousel running order. Same row chrome as the Arrange screen, plus a
   position number and a filename, and narrower arrows since the rows are
   short. */
.bld-carousel-order .frp-group:first-child { margin-top:2px; }
.bld-co-list { display:flex; flex-direction:column; gap:6px; margin-bottom:6px; }
.bld-co-item { grid-template-columns:22px 20px 42px 1fr auto; }
.bld-co-num { font-family:var(--f-mono); font-size:.72rem; color:var(--faint); text-align:center; }
.bld-co-name { font-family:var(--f-mono); font-size:.74rem; color:var(--soft); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.bld-co-item .bld-vs-move { flex-direction:row; gap:3px; }
/* Upload button for one carousel. Sits under that carousel's list rather than
   under both, because the two write to differently-named files and a single
   shared button could not know which you meant. */
.bld-co-upload { margin:2px 0 4px; }
.bld-vs-move .bld-icon-btn { width:26px; height:16px; font-size:.7rem; border-radius:5px; }
.bld-vs-font-row { grid-template-columns:1fr; }
.bld-vs-font-main { display:flex; gap:8px; align-items:center; }
.bld-vs-font-main .bld-vs-font-name { flex:1; }
.bld-font-preview { font-size:1.5rem; line-height:1.25; padding-top:10px; margin-top:2px; border-top:1px dashed var(--line); color:var(--ink); min-height:1.6em; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* ---- Per-font display options (small caps / italic / case / weight / tracking) ---- */
.bld-vs-font-opts { grid-column:1 / -1; display:flex; flex-wrap:wrap; align-items:center; gap:8px 12px; margin-top:10px; padding-top:10px; border-top:1px dashed var(--line); }
.bld-vs-opt { display:flex; align-items:center; gap:6px; font-size:.72rem; font-weight:600; color:var(--soft); white-space:nowrap; }
.bld-vs-opt input[type="checkbox"] { width:15px; height:15px; accent-color:var(--accent); cursor:pointer; }
.bld-vs-opt-sel { width:auto; padding:5px 8px; font-size:.8rem; }
.bld-vs-opt-num { width:62px; padding:5px 8px; font-size:.8rem; }
/* Row header balance: the general Group label is narrow (it doesn't need much),
   the specific Tag field is wide (~half) so custom tags are easy to type. */
.bld-vs-font-row .bld-vs-row-main .bld-vs-label { flex:0 1 38%; }
.bld-vs-tag { flex:1 1 auto; min-width:0; }
.bld-range-row { display:flex; align-items:center; gap:12px; }
.bld-range { flex:1; accent-color:var(--accent); }

/* ---- Site look: full-width mockup cards, one per skin (Page Visual Style
   screen). Each card's mock background/headline/dots are set inline per
   skin from SKIN_TOKENS in builder.js — only the chrome (border, selection
   state, label strip) is styled here. ---- */
.bld-skin-list { display:grid; gap:14px; }
.bld-skin-card { display:block; width:100%; padding:0; appearance:none; cursor:pointer; text-align:start; background:var(--surface); border:2px solid var(--line); border-radius:14px; overflow:hidden; transition:border-color .15s, box-shadow .15s; }
.bld-skin-card:hover { border-color:color-mix(in srgb, var(--accent) 45%, var(--line)); }
.bld-skin-card.is-selected { border-color:var(--accent); box-shadow:0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }
.bld-skin-mock { position:relative; padding:26px 24px; display:flex; flex-direction:column; gap:16px; }
.bld-skin-mock-headline { font-size:1.35rem; line-height:1.3; font-weight:600; }
.bld-skin-mock-dots { display:flex; gap:8px; }
.bld-skin-dot { width:16px; height:16px; border-radius:50%; border:1px solid; opacity:.85; }
.bld-skin-check { position:absolute; top:14px; inset-inline-end:14px; width:24px; height:24px; border-radius:50%; background:var(--accent); opacity:0; transform:scale(.6); transition:opacity .15s, transform .15s; }
.bld-skin-check::after { content:""; position:absolute; inset:0; margin:auto; width:6px; height:10px; border-inline-end:2px solid #fff; border-block-end:2px solid #fff; transform:rotate(45deg) translate(-1px,-2px); }
.bld-skin-card.is-selected .bld-skin-check { opacity:1; transform:scale(1); }
.bld-skin-label { display:block; padding:10px 16px; font-family:var(--f-sans); font-size:.88rem; font-weight:600; color:var(--ink); background:var(--surface); border-top:1px solid var(--line); }
.bld-range-out { font-family:var(--f-mono); font-size:.8rem; color:var(--soft); min-width:52px; text-align:end; flex:none; }

@media (max-width:520px) {
  .bld-cat-names { grid-template-columns:1fr; }
}

/* ---------------------------------------------------------------------------
   404 / not found  (404.php + unknown resource slugs)
   --------------------------------------------------------------------------- */
.notfound { padding:90px 0 100px; }
.notfound .wrap { display:grid; grid-template-columns:300px 1fr; gap:64px; align-items:center; }
.nf-mark { position:relative; aspect-ratio:var(--ar-square); border-radius:20px; border:1px solid var(--line); background:var(--accent-soft); display:grid; place-items:center; overflow:hidden; }
/* Decorative diagonal grid — drawn with a pseudo-element (no empty markup span). */
.nf-mark::before { content:""; position:absolute; inset:0; background-image:repeating-linear-gradient(135deg, transparent 0 15px, color-mix(in srgb,var(--accent) 7%,transparent) 15px 16px); }
.nf-mark .nf-big { position:relative; font-family:var(--f-display); font-weight:700; font-size:clamp(4.2rem,9vw,6rem); letter-spacing:-0.04em; color:var(--accent-text); line-height:1; }
.nf-text .eyebrow { margin-bottom:16px; font-family:var(--f-mono); }
.nf-text h1 { font-size:clamp(2.2rem,5vw,3.4rem); font-weight:600; letter-spacing:-0.03em; line-height:1.02; }
.nf-text p { margin:22px 0 0; font-size:1.12rem; color:var(--ink-2); max-width:46ch; }
.nf-actions { display:flex; gap:12px; margin-top:32px; flex-wrap:wrap; }
.nf-browse { display:inline-block; margin-top:24px; font-size:0.92rem; font-weight:600; color:var(--accent-text); border-bottom:1px solid transparent; padding-bottom:2px; transition:border-color .15s; }
.nf-browse:hover { border-color:var(--accent); }

@media (max-width:760px) {
  /* Mobile: put a field's hint directly under its LABEL rather than under the
     control. On a phone the on-screen keyboard covers everything below the
     input you just tapped, which is exactly where the guidance used to sit —
     so the tip was invisible at the moment you needed it. Desktop keeps the
     hint under the control, where it reads as a footnote. */
  .bld-field { display:flex; flex-direction:column; }
  .bld-field > .bld-label { order:0; }
  .bld-field > .bld-hint  { order:1; }
  .bld-field > :not(.bld-label):not(.bld-hint) { order:2; }

  .notfound { padding:56px 0 72px; }
  .notfound .wrap { grid-template-columns:1fr; gap:36px; }
  .nf-mark { width:160px; aspect-ratio:var(--ar-square); border-radius:16px; }
}

/* ===========================================================================
   RESOURCE DETAIL — hero, reviewer callout, findings grid
   =========================================================================== */

/* Hero: title + summary on the left, screenshot on the right.
   Top-aligned, NOT centred. The copy column changes height when the reviewer
   callout below it opens, and centring re-centred the thumbnail against the new
   height — so the picture slid down the page every time a reviewer opened the
   notes. Aligned to the start, the image is anchored to the top of the h1 and
   nothing under it can move it. */
.detail-hero { display:grid; grid-template-columns:1fr minmax(0,46%); gap:46px; align-items:start; }
.detail-hero-copy { order:1; }
.detail-hero-media { order:2; }
.detail-hero .plate { margin-top:0; }
/* The thumbnail links to the work, captioned with what the work IS. The
   caption sits UNDER the image rather than over it — an overlay would cover
   the slide content that makes the thumbnail worth showing. */
.detail-hero .plate-link { display:block; text-decoration:none; color:inherit; }
.detail-hero .plate-link .plate { display:grid; transition:border-color .16s; }
.detail-hero .plate-link:hover .plate { border-color:var(--accent); }
.plate-cap { display:block; margin-top:10px; }
.plate-cap-main { display:block; font-size:0.9rem; font-weight:650; color:var(--accent-text); }
.plate-cap-go { margin-inline-start:6px; transition:transform .16s; display:inline-block; }
.plate-link:hover .plate-cap-go { transform:translateX(3px); }
.plate-cap-meta { display:block; margin-top:2px; font-size:0.8rem; color:var(--muted); }
.detail-hero-copy h1 { max-width:16ch; }
/* The tool and category, under the title. Quiet on purpose: this is
   classification, and it used to be accent-coloured uppercase directly above an
   h1, which put two shouting things next to each other. Same treatment the spec
   bar gives its own labels, so the page has one language for "this is a label,
   not content". */
.detail-kind { margin-top:10px; font-size:0.82rem; font-weight:600; letter-spacing:0.04em;
  color:var(--label-ink); max-width:52ch; }
.detail-hero-copy .summary { margin-top:20px; max-width:52ch; }

/* On-brand "for reviewers" callout, pinned under the spec bar. Collapsible
   (<details>) and collapsed by default — supporting context, not the headline,
   so it stays out of the way until a reviewer opens it. */
.reviewer-callout { margin-top:22px; background:var(--accent-soft); border:1px solid color-mix(in srgb,var(--accent) 24%,var(--line)); border-inline-start:3px solid var(--accent); border-radius:var(--radius); padding:0; overflow:hidden; }
.reviewer-callout .rc-label { list-style:none; cursor:pointer; display:flex; align-items:center; justify-content:space-between; gap:12px; padding:16px 22px; font-family:var(--f-mono); font-size:0.68rem; font-weight:700; letter-spacing:0.14em; text-transform:uppercase; color:var(--accent-ink); user-select:none; }
.reviewer-callout .rc-label::-webkit-details-marker { display:none; }
.reviewer-callout .rc-chevron { flex:none; width:9px; height:9px; border-right:2px solid var(--accent-ink); border-bottom:2px solid var(--accent-ink); transform:rotate(45deg) translateY(-2px); transition:transform .34s ease; }
.reviewer-callout[open] .rc-chevron { transform:rotate(-135deg) translateY(1px); }
/* Roll open and closed instead of snapping. A <details> cannot animate its own
   height without `interpolate-size`, which is what allows `auto` to be a
   transition endpoint. It has to sit on :root: declaring it on the callout
   itself does NOT reach ::details-content, which was measured — the panel stays
   pinned at block-size 0 forever and never opens at all. Nothing else on this
   site transitions a size to a keyword (the only other one is .settings-panel,
   380px to 640px), so widening the scope changes nothing else.
   `allow-discrete` is what keeps the content painted through the closing half
   of the transition — without it the panel blinks out and only the box
   animates. Browsers without ::details-content get the instant toggle this had
   before, which is a fallback and not a bug. Same for reduced motion, which
   opts out of the whole block deliberately. */
@media (prefers-reduced-motion: no-preference) {
  :root { interpolate-size:allow-keywords; }
  .reviewer-callout::details-content {
    block-size:0; overflow:hidden;
    transition:block-size .34s ease, content-visibility .34s allow-discrete;
  }
  .reviewer-callout[open]::details-content { block-size:auto; }
}
.reviewer-callout ul.clean { gap:9px; padding:0 22px 18px; }
.reviewer-callout ul.clean li { font-size:0.98rem; color:var(--ink-2); }
.reviewer-callout[open] .rc-label { padding-bottom:10px; }

/* Findings: a full-width card grid inside a narrative section. */
.detail-section--wide .section-body { max-width:none; }

/* ---------------------------------------------------------------------------
   Implement / Evaluate — one record at a time
   ---------------------------------------------------------------------------
   Each rollout/round is a stack of ordinary .detail-section fields (see
   renderImplementBody() / renderEvaluateBody() in Resource.php) — the same
   label-left, content-right treatment Analyze and Design use. .rec-section is
   just the grouping wrapper; .rec-switch-bar is the one thing that ISN'T a
   field, pulled out of the section-head it used to share so it can stay
   sticky on its own, independently of whichever field the reader has
   scrolled to. */
.rec-section { padding:34px 0; border-top:1px solid var(--line); }
.phase-divider + .rec-section { border-top:0; padding-top:20px; }

/* Pinned above the fields, at the same offset .section-head uses — so the
   picker never moves once you start reading, exactly as it did when it lived
   inside the section-head. Needs its own background: unlike a section-head
   (which sticks beside a column that never passes behind it), this bar sticks
   ABOVE content that scrolls underneath it. Row layout: name/picker at the
   start, date pushed to the end when there's room, wrapping to its own line
   when there isn't rather than crowding the picker. */
.rec-switch-bar { position:sticky; top:var(--stick-head); z-index:2;
  background:var(--bg); padding:10px 0 16px; margin-bottom:6px;
  display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:8px 16px; }
/* A NUMBERED RECORD heading ("Implementation #1") against a phase section
   heading ("Evaluation"). It is a smaller order of thing: one of several
   rollouts, not one of the five phases. Same treatment the editor gives the
   same string, so the two screens name it identically. Shared by the plain
   <h2> (one record) and the <select> below (several) — same look either way. */
.rec-name { font-family:var(--f-sans); font-size:.82rem; margin:0;
  font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:var(--accent-text); }
/* With several records the picker IS the label — no separate boxed <select>
   repeating the name it sits next to. Unstyled down to plain text, with a
   small disclosure arrow as the only hint it opens a menu. */
select.rec-name { appearance:none; -webkit-appearance:none; -moz-appearance:none;
  background-color:transparent; border:0; border-radius:4px; cursor:pointer;
  padding:2px 20px 2px 4px; margin-inline-start:-4px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2362636F' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 2px center; }
[dir="rtl"] select.rec-name { background-position:left 2px center; padding:2px 4px 2px 20px; }
select.rec-name:focus-visible { outline:none; box-shadow:0 0 0 3px var(--accent-soft); }
.rec-when { font-size:.9rem; color:var(--soft); }
/* Only ever hides where scripting can un-hide. Without .has-js every record
   stays on the page, stacked — which is what a crawler and a no-JS reader get. */
body.has-js .rec-body[hidden] { display:none; }
/* The second sticky tier: whichever field is on screen pins right under the
   switch bar rather than at the page's usual --stick-head offset, so the two
   never overlap. --rec-bar-h is set from the switch bar's real rendered
   height (varies with 1 vs several rollouts, and with translated label
   length, which can wrap the date to its own line) — see the ResizeObserver
   in phpsite.js. */
.rec-body .detail-section .section-head { top:calc(var(--stick-head) + var(--rec-bar-h, 40px)); }

/* The facts of one rollout. Label over value, the same treatment the spec bar
   uses at the top of the page, so the two read as the same kind of thing. */
.impl-facts { display:grid; grid-template-columns:repeat(auto-fit,minmax(160px,1fr)); gap:18px 26px; margin:0 0 22px; }
.impl-fact dt { font-size:.66rem; font-weight:600; letter-spacing:.14em; text-transform:uppercase; color:var(--label-ink); margin-bottom:5px; }
.impl-fact dd { margin:0; font-size:1rem; color:var(--ink); }
/* What actually went out. Small, because it is provenance rather than content. */
.impl-versions { display:flex; flex-wrap:wrap; gap:6px 10px; margin:0 0 22px; padding:11px 14px;
  background:var(--surface); border:1px solid var(--line); border-radius:11px; font-size:.86rem; color:var(--soft); }
.impl-versions .iv-label { width:100%; font-size:.66rem; font-weight:600; letter-spacing:.14em; text-transform:uppercase; color:var(--label-ink); }
.impl-versions .iv-item { color:var(--ink-2); }
.impl-versions .iv-v { font-family:var(--f-mono); font-size:.78rem; font-weight:700; color:var(--accent-text); }
.impl-versions .iv-sep { color:var(--faint); }
.plain-list { margin:0 0 8px; padding-inline-start:1.25em; }
.plain-list li { margin-bottom:8px; }
.plain-list li:last-child { margin-bottom:0; }

/* Issues by priority. The priority is NAMED in the first cell, so colour is a
   second channel on a labelled row and never the only carrier. Ink is pinned
   per theme — these tints stop being backgrounds --ink can sit on. */
.sb-table tr.sev td { background:var(--sev-bg); color:var(--sev-ink); }
.sb-table tr.sev td:first-child { font-weight:700; }
.sb-table tr.sev-critical { --sev-bg:#FDE7E4; --sev-ink:#16161F; }
.sb-table tr.sev-major    { --sev-bg:#FDF3D6; --sev-ink:#16161F; }
.sb-table tr.sev-minor    { --sev-bg:#E6F0FC; --sev-ink:#16161F; }
.sb-table tr.sev-normal   { --sev-bg:transparent; --sev-ink:var(--ink); }
html[data-theme="dark"] .sb-table tr.sev-critical { --sev-bg:#4A2320; --sev-ink:#F4E9E7; }
html[data-theme="dark"] .sb-table tr.sev-major    { --sev-bg:#46391A; --sev-ink:#F5EFE2; }
html[data-theme="dark"] .sb-table tr.sev-minor    { --sev-bg:#1E3350; --sev-ink:#E5EDF7; }

/* Results. Stat tiles, not a chart: a handful of headline numbers with no time
   axis. Proportional figures, not tabular — tabular-nums gives every digit the
   width of a 0, which reads loose at this size and nothing here aligns down a
   column. */
.stat-row { display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:12px; margin:0 0 8px; }
.stat { padding:16px 18px; background:var(--surface); border:1px solid var(--line); border-radius:12px; }
.stat-label { font-size:.8rem; color:var(--soft); margin-bottom:6px; }
.stat-value { font-family:var(--f-sans); font-size:2rem; font-weight:600; line-height:1.1; color:var(--ink); letter-spacing:-0.02em; }

/* A quote is somebody's voice, so it gets a rule and italics rather than a
   card — cards read as UI, and this is testimony. */
.quotes { display:grid; gap:16px; margin:0 0 8px; }
.quote { margin:0; padding:2px 0 2px 18px; border-inline-start:3px solid var(--accent-soft); }
.quote p { margin:0; font-size:1.06rem; font-style:italic; color:var(--ink-2); }
.quote-src { font-size:.72rem; font-weight:600; letter-spacing:.14em; text-transform:uppercase; color:var(--label-ink); margin:20px 0 9px; }

/* Whether the objectives were met. The WORD carries the state; the colour and
   the dot are a second channel, never the only one. */
.met { display:inline-flex; align-items:center; gap:9px; padding:9px 15px; border-radius:999px;
  font-size:.9rem; font-weight:700; margin:0 0 14px; }
.met-dot { width:9px; height:9px; border-radius:50%; background:currentColor; flex:none; }
.met-yes  { color:var(--ok);   background:color-mix(in srgb, var(--ok) 14%, transparent); }
.met-part { color:var(--warn); background:color-mix(in srgb, var(--warn) 14%, transparent); }
.met-no   { color:#C0392B;     background:color-mix(in srgb, #C0392B 14%, transparent); }
html[data-theme="dark"] .met-no { color:#F0857A; }
.findings { display:flex; flex-direction:column; gap:22px; }
.finding-row { display:flex; gap:22px; justify-content:center; flex-wrap:wrap; }
.finding-card { flex:1 1 300px; min-width:0; max-width:560px; display:flex; flex-direction:column; background:var(--surface); border:1px solid var(--line); border-radius:14px; overflow:hidden; box-shadow:0 1px 3px rgba(22,22,31,0.04); }
.finding-band { background:var(--fb,var(--accent)); color:var(--fb-ink,#fff); font-family:var(--f-display); font-weight:700; font-size:1.05rem; letter-spacing:-0.01em; padding:12px 20px; }
.finding-claim { flex:1; padding:20px; font-size:1.1rem; line-height:1.4; font-weight:500; color:var(--ink); }
.finding-claim strong { font-weight:700; }
.finding-evidence { background:color-mix(in srgb,var(--fb,var(--accent)) 12%,var(--surface)); border-top:1px solid var(--line); padding:16px 20px; }
.finding-evidence ul { list-style:none; margin:0; padding:0; display:grid; gap:10px; }
.finding-evidence li { position:relative; padding-inline-start:18px; font-size:0.9rem; line-height:1.45; color:var(--ink-2); }
.finding-evidence li::before { content:""; position:absolute; inset-inline-start:2px; top:0.6em; width:6px; height:6px; border-radius:50%; background:var(--fb,var(--accent)); }

/* Collapsible on small screens (see phpsite.js). On desktop it's forced open
   and the toggle is hidden; on mobile it starts collapsed and the toggle shows. */
.findings-collapse { display:block; }
.findings-collapse > summary { list-style:none; cursor:pointer; }
.findings-collapse > summary::-webkit-details-marker { display:none; }
.findings-toggle { display:none; align-items:center; gap:10px; width:100%; padding:13px 16px; background:var(--surface); border:1px solid var(--line); border-radius:12px; font-family:var(--f-sans); font-weight:600; font-size:0.98rem; color:var(--ink); }
.findings-toggle .ft-count { display:inline-grid; place-items:center; min-width:22px; height:22px; padding:0 6px; border-radius:11px; background:var(--accent-soft); color:var(--accent-ink); font-size:0.78rem; font-weight:700; }
.findings-toggle .ft-chevron { margin-inline-start:auto; width:9px; height:9px; border-right:2px solid var(--soft); border-bottom:2px solid var(--soft); transform:rotate(45deg); transition:transform .2s ease; }
.findings-collapse[open] .findings-toggle .ft-chevron { transform:rotate(-135deg); }
.findings-collapse[open] .findings-toggle { margin-bottom:22px; }

@media (max-width:860px) {
  .detail-hero { grid-template-columns:1fr; gap:26px; }
  /* Image BELOW the title and description on a phone. It used to be order:-1,
     which put a 16:9 picture above the words — so the first screenful was a
     thumbnail and the reader had to scroll to learn what they were looking at.
     On a single column the title has to come first; the image is now the
     payoff at the end of the hero rather than a gate in front of it. */
  .detail-hero-media { order:2; }
  .finding-card { max-width:none; }
}

/* ---- Public version history ------------------------------------------------
   The superseded builds of one artefact, reachable by a visitor. Quiet by
   design: it is provenance sitting under the thing itself, so it stays closed,
   stays small, and never competes with the current version's own row. Same
   chevron language as the reviewer callout so the two read as one kind of
   control. */
.vh { margin:6px 0 0 44px; }
.vh-sum { display:inline-flex; align-items:center; gap:7px; cursor:pointer; list-style:none;
  font-family:var(--f-mono); font-size:.68rem; font-weight:700; letter-spacing:.08em;
  text-transform:uppercase; color:var(--soft); padding:3px 0; }
.vh-sum::-webkit-details-marker { display:none; }
.vh-sum::after { content:""; width:7px; height:7px; border-right:2px solid currentColor;
  border-bottom:2px solid currentColor; transform:rotate(45deg) translateY(-2px); transition:transform .28s ease; }
.vh[open] .vh-sum::after { transform:rotate(-135deg) translateY(1px); }
.vh-sum:hover { color:var(--accent-text); }
.vh-list { list-style:none; margin:6px 0 2px; padding:0; display:grid; gap:2px; }
.vh-item { display:flex; gap:10px; align-items:baseline; text-decoration:none;
  padding:6px 9px; border-radius:8px; border:1px solid transparent; }
.vh-item:hover { background:var(--surface); border-color:var(--line); }
.vh-v { flex:none; font-family:var(--f-mono); font-size:.72rem; font-weight:700; color:var(--accent-text); }
.vh-body { display:flex; flex-direction:column; gap:1px; min-width:0; }
.vh-note { font-size:.86rem; color:var(--ink-2); }
.vh-when { font-size:.74rem; color:var(--faint); }
@media (max-width:860px) { .vh { margin-inline-start:0; } }

/* ---- Builder: "put the next version here" (owner Settings panel) ----
   An instruction the owner has to act on OUTSIDE this screen, by FTP, so it is
   given the weight of a callout rather than a hint: a grey line under a field
   is read as background, and this one has to be carried to another program. */
.bld-scan { margin-top:10px; padding:11px 13px; border:1px solid var(--line);
  border-inline-start:3px solid var(--accent); border-radius:9px; background:var(--bg); }
.bld-scan-path { font-family:var(--f-mono); font-size:.78rem; color:var(--ink); word-break:break-all; }
.bld-scan-why  { margin-top:5px; font-size:.78rem; color:var(--soft); }
.bld-scan-warn { margin-top:6px; font-size:.78rem; font-weight:600; color:var(--warn); }


/* ---- Storyboard: table on a desk, one step at a time on a phone -------------
   Both views are always in the DOM (scripts/table.js builds them from one
   model); this is the only thing that decides which is on screen. A media query
   rather than JS, so rotating the phone costs nothing and nothing re-renders. */
.sb-cards { display:none; }
@media (max-width:700px) {
  .tbl-scroll { display:none; }
  .sb-cards { display:block; }
  .sb-skipnav { display:none; }
}
.sb-pager { margin:0 0 14px; }
.sb-card { background:var(--surface); border:1px solid var(--line); border-radius:14px; padding:16px 18px; }
.sb-card[hidden] { display:none; }
.sb-card-title { font-family:var(--f-sans); font-size:.82rem; font-weight:700; letter-spacing:.1em;
  text-transform:uppercase; color:var(--accent-text); margin-bottom:12px; }
.sb-card dl { margin:0; display:grid; gap:12px; }
.sb-card dt { font-size:.66rem; font-weight:600; letter-spacing:.14em; text-transform:uppercase;
  color:var(--label-ink); margin-bottom:3px; }
.sb-card dd { margin:0; font-size:.98rem; line-height:1.5; color:var(--ink); }

/* The step name stays put while the rest of a wide storyboard scrolls past it.
   Without it, scrolling right on a tablet loses which row you are reading —
   the first column is the only thing that says. */
.sb-table tbody td:first-child, .sb-table tr td:first-child,
.sb-table thead th:first-child, .sb-table tr:first-child th:first-child {
  position:sticky; inset-inline-start:0; z-index:1; background:var(--surface); }
.sb-table thead th:first-child, .sb-table tr:first-child th:first-child { background:var(--accent-soft); z-index:2; }
.sb-table tr.sev td:first-child { background:var(--sev-bg); }

/* The ADDIE nav stands down while a storyboard is on screen. It is 45px of a
   116px sticky stack, and on a phone that stack was competing with the very
   thing it was helping you navigate to. Set by scripts/phpsite.js. */
body.sb-focus .addie-nav { transform:translateY(-100%); pointer-events:none; }
.addie-nav { transition:transform .24s ease; }
@media (prefers-reduced-motion: reduce) { .addie-nav { transition:none; } }

/* ---- Builder: unsaved-work rescue (owner Settings panel) --------------------
   Two strips that sit at the top of the form. They are deliberately different
   weights, because they are different news: the RESTORE offer is a decision the
   owner has to make before carrying on, so it takes the accent and reads as a
   card; the idle NUDGE is only advice and the work is still safe, so it stays
   quiet and can be dismissed without doing anything. */
.bld-restore { margin:0 0 16px; padding:14px 16px; border-radius:11px;
  border:1px solid color-mix(in srgb, var(--accent) 30%, var(--line));
  border-inline-start:3px solid var(--accent); background:var(--accent-soft); }
.bld-restore-t { font-size:.9rem; font-weight:700; color:var(--accent-ink); margin-bottom:4px; }
.bld-restore-b { font-size:.86rem; line-height:1.5; color:var(--ink-2); }
.bld-restore-btns { display:flex; flex-wrap:wrap; gap:8px; margin-top:12px; }
.bld-nudge { display:flex; align-items:flex-start; gap:12px; margin:0 0 14px;
  padding:11px 13px; border-radius:9px; border:1px solid var(--line);
  background:var(--surface); font-size:.84rem; line-height:1.5; color:var(--soft); }
.bld-nudge-x { flex:none; margin-inline-start:auto; appearance:none; cursor:pointer;
  background:none; border:0; padding:2px 4px; font-family:var(--f-sans);
  font-size:.78rem; font-weight:600; color:var(--accent-text); }
.bld-nudge-x:hover { text-decoration:underline; }

/* ---- Builder: findings editor (owner Settings panel) ---- */
.bld-finding-card { border:1px solid var(--line); border-radius:12px; padding:14px 16px; margin-bottom:12px; background:var(--bg); }
.bld-finding-head { display:flex; align-items:center; justify-content:space-between; margin-bottom:6px; }
.bld-finding-num { font-family:var(--f-display); font-weight:700; font-size:0.9rem; color:var(--accent-ink); }
.bld-sub-label { display:block; font-size:0.68rem; font-weight:700; letter-spacing:0.08em; text-transform:uppercase; color:var(--soft); margin:12px 0 6px; }
.bld-add-sm { font-size:0.8rem; padding:6px 11px; margin-top:8px; }

/* Parts editor. Reuses the findings card so a repeatable row looks the same
   wherever it appears in the builder; only the header tools and the two
   side-by-side selects are new. */
.bld-part-tools { display:flex; align-items:center; gap:8px; }
.bld-part-lead { font-family:var(--f-mono); font-size:.6rem; letter-spacing:.1em; text-transform:uppercase;
  color:var(--accent-text); background:var(--accent-soft); border-radius:5px; padding:3px 7px; margin-inline-start:10px; }
.bld-part-pair { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
@media (max-width:560px){ .bld-part-pair { grid-template-columns:1fr; gap:0; } }

/* ===========================================================================
   RIGHT-TO-LEFT LANGUAGES (Hebrew, and any future [dir="rtl"] locale)
   ---------------------------------------------------------------------------
   Most of the site's layout already mirrors correctly for free: flexbox/grid
   follow the inline direction, and .wrap/most spacing already uses logical
   properties (margin-inline, padding-inline) or was converted to them above
   (border-inline-*, inset-inline-*, text-align:start, etc.) specifically so
   this block could stay small. What's left here is only what CAN'T be
   expressed as a logical property: transforms (which don't have a
   direction-aware equivalent) and static "→"/"←" glyphs baked into markup,
   which are visual arrows, not simply positioned content.
   =========================================================================== */
[dir="rtl"] .addie-nav a::after { transform-origin:right; }
[dir="rtl"] .proj-row:not(.placeholder):hover .proj-arrow .ar { transform:scaleX(-1) translateX(6px); }
@media (prefers-reduced-motion: reduce) {
  [dir="rtl"] .proj-row:not(.placeholder):hover .proj-arrow .ar { transform:scaleX(-1) translateX(4px); }
}
/* "←" in the back-links and "→" on project-card arrows (.ar) are directional
   glyphs, not decoration — scaleX(-1) flips the character itself so "back"
   still visually points toward where you came from and "view resource"
   still points toward where the link goes, in whichever direction that is. */
[dir="rtl"] .back-arrow, [dir="rtl"] .proj-arrow .ar { display:inline-block; transform:scaleX(-1); }
/* The persistent hero link, the full-resource cover, the hero caption and the
   parts rows all carry a directional glyph too — "↗" for "opens away from
   here" and "→" for "this way". Same treatment and same reason as the
   back-links above: flip the CHARACTER, so "away" still reads as away when the
   page runs right-to-left. Missing these left every new arrow on the resource
   page pointing back into the text in Hebrew.

   The ::after arrows sit inside a flipped box, so transform-origin doesn't
   matter; the inline ones need display:inline-block for the transform to
   apply at all. */
[dir="rtl"] .addie-nav a.addie-nav-open .ano-label::after,
[dir="rtl"] .fr-follow .frf-label::after,
[dir="rtl"] .fr-cover.is-external .frc-cap-main::after { display:inline-block; transform:scaleX(-1); }
[dir="rtl"] .plate-cap-go,
[dir="rtl"] .frp-go { display:inline-block; transform:scaleX(-1); }
/* The hover nudge travels with the reader, so it has to reverse as well —
   translateX is physical and would otherwise push the arrow backwards. */
[dir="rtl"] .plate-link:hover .plate-cap-go { transform:scaleX(-1) translateX(3px); }

/* Carousel prev/next (media.js) reuse the same ‹ / › glyphs for both
   directions — inset-inline-start/end above already swapped which SIDE each
   button sits on; this flips the glyph so it still points the right way. */
[dir="rtl"] .carousel-arrow { transform:translateY(-50%) scaleX(-1); }

/* ---- Publishing recipe under the resource-type picker ---- */
.bld-recipe { margin-top:12px; border-left:3px solid var(--accent-soft); padding:2px 0 2px 14px; }
.bld-recipe-head { font-family:var(--f-mono); font-size:.66rem; letter-spacing:.14em; text-transform:uppercase; color:var(--faint); margin-bottom:8px; }
.bld-recipe ol { margin:0; padding-left:18px; }
.bld-recipe li { font-size:.86rem; line-height:1.55; color:var(--soft); margin-bottom:7px; }
.bld-recipe-warn { font-size:.84rem; line-height:1.55; color:var(--warn); margin:10px 0 0; font-weight:500; }

/* ===== The persistent hero link =========================================
   Two presentations of one destination. WHICH one is decided by measurement at
   runtime, not by a width guess — scripts/phpsite.js puts `fr-bar-fits` on
   <html> when the ADDIE bar genuinely has room for the item, and takes it off
   when it doesn't.

   A fixed breakpoint was wrong here and looked it. The bar's spare room depends
   on how many ADDIE phases this particular resource has (three links or six),
   on the skin (Bold docks its nav into a fixed sidebar and loses ~145px of
   content width; the others don't), and on the rendered width of the label in
   whatever language is active. One number cannot express that, so a number
   tuned for the worst case sent every other case to the pill with half the bar
   still empty. */
.addie-nav a.addie-nav-open { display:none; }
:root.fr-bar-fits .addie-nav a.addie-nav-open {
  display:flex; align-items:center; gap:10px; margin-inline-start:auto;
  flex:none; padding:6px 12px 6px 8px; border:1.5px solid var(--line); border-radius:999px;
  text-decoration:none; color:var(--soft); font-weight:600; font-size:.86rem; white-space:nowrap;
  transition:color .16s, border-color .16s, background .16s;
}
:root.fr-bar-fits .addie-nav a.addie-nav-open:hover { color:var(--on-accent); background:var(--accent); border-color:var(--accent); }
.addie-nav a.addie-nav-open .ano-thumb { flex:none; width:28px; height:18px; border-radius:4px; overflow:hidden; background:var(--hair); display:block; }
.addie-nav a.addie-nav-open .ano-thumb img { width:100%; height:100%; object-fit:cover; display:block; }
.addie-nav a.addie-nav-open .ano-label::after { content:" \2197"; }
/* The phase links yield first as the bar narrows — they already scroll. */
.addie-nav ul { min-width:0; }

/* The pill, for every case where the bar item does not fit. Hidden until the
   hero has scrolled out of view (see syncFollow in scripts/phpsite.js), and it
   stands down near the footer so it never covers those links. */
.fr-follow { position:fixed; inset-block-end:20px; inset-inline-end:20px; z-index:70;
  display:flex; align-items:center; gap:10px; padding:8px 14px 8px 10px;
  border:1.5px solid var(--line); border-radius:999px; background:var(--surface);
  box-shadow:0 10px 30px -12px rgba(0,0,0,.4); text-decoration:none;
  color:var(--ink); font-weight:600; font-size:.88rem; white-space:nowrap;
  opacity:0; transform:translateY(8px); pointer-events:none;
  transition:opacity .22s, transform .22s, border-color .16s; }
.fr-follow.is-on { opacity:1; transform:none; pointer-events:auto; }
.fr-follow:hover { border-color:var(--accent); }
.fr-follow .frf-thumb { flex:none; width:34px; height:22px; border-radius:5px; overflow:hidden;
  background:var(--hair); display:block; }
.fr-follow .frf-thumb img { width:100%; height:100%; object-fit:cover; display:block; }
.fr-follow .frf-label::after { content:" \2197"; }
:root.fr-bar-fits .fr-follow { display:none; }
@media (prefers-reduced-motion:reduce) { .fr-follow { transition:opacity .01s; transform:none; } }

/* ===== The kit, in the header ============================================
   Only the parts list lives up here now; the embed went back to the end of
   Develop (see .fr-cover below). */
.fr-lead { margin:18px 22px 0; }

/* ===== The full resource, closing Develop ================================
   One cover image, two behaviours. Embeddable: a <button> that swaps itself
   for the iframe. Not embeddable: an <a> to a new tab. Identical to look at,
   because the reader should not have to care which one they got — and the
   choice was measured on the server, not guessed here. */
.fr-cover { display:block; width:100%; padding:0; border:0; background:none;
  font:inherit; color:inherit; text-align:start; cursor:pointer; text-decoration:none; }
.fr-cover .frc-plate { position:relative; display:block; aspect-ratio:var(--ar-video);
  border:1px solid var(--line); border-radius:18px; overflow:hidden; background:var(--accent-soft);
  background-image:repeating-linear-gradient(135deg, transparent 0 18px, color-mix(in srgb,var(--accent) 6%,transparent) 18px 19px);
  display:grid; place-items:center; transition:border-color .16s; }
.fr-cover .frc-plate img { width:100%; height:100%; object-fit:cover; display:block; }
.fr-cover:hover .frc-plate, .fr-cover:focus-visible .frc-plate { border-color:var(--accent); }
.fr-cover:focus-visible { outline:2px solid var(--accent); outline-offset:4px; border-radius:18px; }
/* The affordance: a soft disc with a triangle for "open this". Sits over the
   image, centred, and grows very slightly on hover. */
.frc-badge { position:absolute; inset:50% auto auto 50%; translate:-50% -50%;
  width:64px; height:64px; border-radius:50%; background:color-mix(in srgb, var(--surface) 88%, transparent);
  border:1.5px solid var(--line); backdrop-filter:blur(6px);
  display:grid; place-items:center; transition:transform .18s, border-color .18s; }
/* Play — video only. The triangle is the loudest affordance on the page and it
   promises playback, so it may not appear on a PDF or a slide deck. */
.frc-badge--play::before { content:""; width:0; height:0; margin-inline-start:4px;
  border-block:11px solid transparent; border-inline-start:18px solid var(--accent); }
/* Opens in place: two arrows pushing apart, i.e. "expand this here". */
.frc-badge--open::before { content:""; width:20px; height:20px; background:var(--accent);
  -webkit-mask:var(--icon-expand) center / contain no-repeat; mask:var(--icon-expand) center / contain no-repeat; }
/* Leaves the site: the same corner arrow the caption already appends. */
.frc-badge--external::before { content:""; width:19px; height:19px; background:var(--accent);
  -webkit-mask:var(--icon-external) center / contain no-repeat; mask:var(--icon-external) center / contain no-repeat; }
:root {
  --icon-expand:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 3h6v6'/%3E%3Cpath d='M9 21H3v-6'/%3E%3Cpath d='M21 3l-7 7'/%3E%3Cpath d='M3 21l7-7'/%3E%3C/svg%3E");
  --icon-external:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 17L17 7'/%3E%3Cpath d='M8 7h9v9'/%3E%3C/svg%3E");
}
.fr-cover:hover .frc-badge { transform:scale(1.06); border-color:var(--accent); }
.frc-cap { display:block; margin-top:12px; }
.frc-cap-main { display:block; font-family:var(--f-display); font-weight:600; font-size:1.05rem; color:var(--accent-text); }
.fr-cover.is-external .frc-cap-main::after { content:" \2197"; }
.frc-cap-meta { display:block; margin-top:2px; font-size:.86rem; color:var(--soft); }
.fr-embed .fr-wrap[hidden] { display:none; }

/* The other artefacts, grouped by who uses them. */
.fr-parts { margin-top:18px; }
.frp-group { font-size:.66rem; font-weight:600; letter-spacing:.14em; text-transform:uppercase; color:var(--label-ink); margin:20px 0 9px; }
.frp-group:first-child { margin-top:6px; }
.frp-list { list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:8px; }
.frp { display:flex; align-items:center; gap:14px; padding:12px 15px; background:var(--surface);
  border:1px solid var(--line); border-radius:12px; text-decoration:none; transition:border-color .16s; }
.frp:hover { border-color:var(--accent); }
.frp-kind { flex:none; width:38px; height:38px; border-radius:9px; display:grid; place-items:center;
  font-family:var(--f-mono); font-size:.58rem; font-weight:700; background:var(--accent-soft); color:var(--accent-ink); }
.frp-kind.is-deck { background:#FFEFD6; color:#8A5A12; }
.frp-kind.is-doc  { background:#DFF2E6; color:#1F6B41; }
.frp-kind.is-aid,.frp-kind.is-pdf { background:#E7E9FB; color:#3A3597; }
.frp-body { min-width:0; display:flex; flex-direction:column; }
.frp-title { font-weight:600; font-size:.95rem; color:var(--ink); }
.frp-meta { font-size:.82rem; color:var(--soft); margin-top:1px; }
/* Which version of this part a visitor is being handed. Provenance, not
   content, so it takes the same quiet mono treatment the implementation
   versions strip uses rather than competing with the part's own name. Rendered
   only when the part HAS a stack, so an unversioned kit looks exactly as it
   always did. */
.frp-v { display:inline-block; margin-inline-start:8px; font-family:var(--f-mono);
  font-size:.7rem; font-weight:700; letter-spacing:.06em; color:var(--accent-text);
  vertical-align:1px; }
.frp-go { margin-inline-start:auto; flex:none; color:var(--accent-text); font-size:1rem; }

@media (max-width:760px){
  .fr-lead { margin-inline:0; }
}
