DIY — Repair Q&A Thread
A complete community repair troubleshooting thread built as one self-contained page: a question card with device chip, reputation badges, an SVG discharge chart and a working vote control, then four ranked answers where the top one carries a green chosen-solution banner. Sort tabs reorder the list with a FLIP animation, comment sub-threads collapse under each answer, and the composer wraps your selection with a real formatting toolbar before optimistically prepending the posted answer.
MCP
Code
:root{
--primary:#0f7fa8;
--primary-d:#0b6488;
--primary-l:#e4f2f8;
--green:#3fa34d;
--green-d:#2f8a3c;
--green-soft:#e6f5e8;
--ink:#12212b;
--ink-2:#33454f;
--muted:#6a7b86;
--bg:#ffffff;
--surface:#f2f5f7;
--surface-2:#e8eef2;
--white:#ffffff;
--line:rgba(18,33,43,.12);
--line-2:rgba(18,33,43,.22);
--gold:#c08a1e;
--silver:#7a8a95;
--bronze:#a8703c;
--danger:#c8442f;
--r-sm:6px; --r-md:12px; --r-lg:18px;
--sh-1:0 1px 2px rgba(18,33,43,.06), 0 2px 8px rgba(18,33,43,.05);
--sh-2:0 6px 24px rgba(18,33,43,.10);
}
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
margin:0;
font-family:Inter,system-ui,-apple-system,Segoe UI,sans-serif;
font-size:15px;
line-height:1.5;
color:var(--ink);
background:var(--surface);
-webkit-font-smoothing:antialiased;
-moz-osx-font-smoothing:grayscale;
}
h1,h2,h3{margin:0;line-height:1.25;letter-spacing:-.015em}
p{margin:0 0 .8em}
a{color:var(--primary);text-decoration:none}
a:hover{text-decoration:underline}
button{font:inherit;color:inherit}
:focus-visible{outline:2.5px solid var(--primary);outline-offset:2px;border-radius:4px}
.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.skip{position:absolute;left:-999px;top:8px;background:var(--ink);color:#fff;padding:8px 14px;border-radius:var(--r-sm);z-index:50}
.skip:focus{left:8px}
.mono{font-family:"JetBrains Mono",ui-monospace,monospace}
.muted{color:var(--muted)}
.small{font-size:12.5px}
.grow{flex:1}
.wrap{max-width:1180px;margin:0 auto;padding:0 20px}
/* ---------- topbar ---------- */
.topbar{
position:sticky;top:0;z-index:30;
background:rgba(255,255,255,.92);
backdrop-filter:saturate(160%) blur(10px);
border-bottom:1px solid var(--line);
}
.topbar-in{display:flex;align-items:center;gap:20px;height:62px}
.brand{display:flex;align-items:center;gap:9px;color:var(--ink);text-decoration:none;flex:none}
.brand:hover{text-decoration:none}
.brand-mark{display:flex;filter:drop-shadow(0 2px 5px rgba(15,127,168,.3))}
.brand-text{font-weight:800;letter-spacing:-.03em;font-size:17px}
.brand-sub{font-weight:500;color:var(--primary);margin-left:5px;letter-spacing:0}
.topnav{display:flex;gap:4px}
.topnav a{
padding:7px 12px;border-radius:999px;color:var(--ink-2);
font-weight:600;font-size:13.5px;transition:background .15s,color .15s;
}
.topnav a:hover{background:var(--surface);text-decoration:none}
.topnav a.on{background:var(--primary-l);color:var(--primary-d)}
.topbar-right{margin-left:auto;display:flex;align-items:center;gap:10px}
.searchbox{
display:flex;align-items:center;gap:8px;
background:var(--surface);border:1px solid transparent;
border-radius:999px;padding:0 14px;height:36px;color:var(--muted);
transition:border-color .15s,background .15s;
}
.searchbox:focus-within{background:#fff;border-color:var(--primary)}
.searchbox input{border:0;background:transparent;outline:none;font:inherit;font-size:13.5px;width:190px;color:var(--ink)}
.avatar{
width:30px;height:30px;border-radius:50%;flex:none;
display:grid;place-items:center;
background:linear-gradient(150deg,var(--primary),#12617f);
color:#fff;font-size:11px;font-weight:700;letter-spacing:.02em;
}
.avatar-gold{background:linear-gradient(150deg,#d9a53c,#a8720f)}
.avatar-me{background:linear-gradient(150deg,#4c6675,#2b3d48)}
.avatar-xs{width:24px;height:24px;font-size:9.5px}
/* ---------- buttons ---------- */
.btn{
border:1px solid transparent;border-radius:var(--r-sm);
padding:8px 15px;font-size:13.5px;font-weight:600;cursor:pointer;
transition:transform .1s,background .15s,box-shadow .15s,border-color .15s;
}
.btn:active{transform:translateY(1px)}
.btn-primary{background:var(--primary);color:#fff;box-shadow:0 2px 8px rgba(15,127,168,.28)}
.btn-primary:hover{background:var(--primary-d)}
.btn-primary:disabled{background:var(--surface-2);color:var(--muted);box-shadow:none;cursor:not-allowed;transform:none}
.btn-ghost{background:transparent;border-color:var(--line-2);color:var(--ink-2)}
.btn-ghost:hover{background:var(--surface);border-color:var(--primary)}
.btn-outline{background:#fff;border-color:var(--line-2);color:var(--primary-d)}
.btn-outline:hover{border-color:var(--primary);background:var(--primary-l)}
.block{display:block;width:100%;text-align:center;margin-top:12px}
.linkbtn{
background:none;border:0;padding:4px 6px;margin:-4px -6px;
color:var(--muted);font-size:12.5px;font-weight:600;cursor:pointer;border-radius:5px;
}
.linkbtn:hover{color:var(--primary-d);background:var(--primary-l)}
/* ---------- breadcrumb + layout ---------- */
.crumbs{display:flex;flex-wrap:wrap;gap:7px;align-items:center;font-size:12.5px;color:var(--muted);padding-top:16px;padding-bottom:2px}
.crumbs a{color:var(--muted);font-weight:500}
.crumbs a:hover{color:var(--primary)}
.crumbs [aria-current]{color:var(--ink);font-weight:600}
.layout{display:grid;grid-template-columns:minmax(0,1fr) 316px;gap:22px;padding-top:16px;padding-bottom:48px;align-items:start}
.col-main{display:flex;flex-direction:column;gap:18px;min-width:0}
.col-side{display:flex;flex-direction:column;gap:16px;position:sticky;top:78px}
.card{background:var(--white);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--sh-1)}
/* ---------- question ---------- */
.question{display:grid;grid-template-columns:56px minmax(0,1fr);gap:6px;padding:22px 24px 18px}
.q-body{min-width:0}
.q-top{display:flex;flex-wrap:wrap;gap:7px;margin-bottom:10px}
.chip{
display:inline-flex;align-items:center;gap:6px;
font-size:12px;font-weight:600;padding:4px 10px;border-radius:999px;
background:var(--surface);color:var(--ink-2);border:1px solid var(--line);
font-family:"JetBrains Mono",monospace;letter-spacing:-.01em;
}
.chip-device{background:var(--primary-l);border-color:rgba(15,127,168,.28);color:var(--primary-d);cursor:pointer}
.chip-device:hover{background:#d6ebf4}
.chip .dot{width:6px;height:6px;border-radius:50%;background:var(--primary)}
.pill{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:700;padding:4px 10px;border-radius:999px;text-transform:uppercase;letter-spacing:.05em}
.pill-solved{background:var(--green-soft);color:var(--green-d);border:1px solid rgba(63,163,77,.3)}
.question h1{font-size:26px;font-weight:800;margin-bottom:10px}
.meta{display:flex;flex-wrap:wrap;align-items:center;gap:8px;font-size:12.5px;color:var(--muted);margin-bottom:14px}
.meta .author{font-weight:700;color:var(--primary-d)}
.sep{opacity:.5}
.badge{font-size:11px;font-weight:700;padding:2px 8px;border-radius:999px;font-family:"JetBrains Mono",monospace}
.badge-gold{background:#fbf1dc;color:var(--gold)}
.badge-silver{background:var(--surface);color:var(--silver)}
.badge-bronze{background:#f7ede4;color:var(--bronze)}
.badge-flat{background:var(--primary-l);color:var(--primary-d)}
.prose{color:var(--ink-2);font-size:14.6px}
.prose strong{color:var(--ink)}
.prose ul,.prose ol{margin:0 0 .9em;padding-left:20px}
.prose li{margin-bottom:.3em}
.prose code{font-family:"JetBrains Mono",monospace;font-size:.88em;background:var(--surface);padding:1px 5px;border-radius:4px;color:var(--ink)}
.code{
margin:0 0 1em;padding:14px 16px;overflow-x:auto;
background:#0e1c25;color:#d6e6ee;border-radius:var(--r-md);
font-family:"JetBrains Mono",monospace;font-size:12.6px;line-height:1.65;
border-left:3px solid var(--primary);
}
.code code{background:none;padding:0;color:inherit;font-size:inherit}
.fig{margin:4px 0 14px}
.fig svg{width:100%;height:auto;display:block;border:1px solid var(--line);border-radius:var(--r-md)}
.fig figcaption{margin-top:8px;font-size:12.3px;color:var(--muted)}
.q-actions{display:flex;flex-wrap:wrap;align-items:center;gap:14px;padding-top:14px;margin-top:6px;border-top:1px solid var(--line)}
/* ---------- vote ---------- */
.vote{display:flex;flex-direction:column;align-items:center;gap:2px;padding-top:2px}
.vote button{
width:34px;height:30px;display:grid;place-items:center;
background:none;border:0;border-radius:8px;color:#a9b7c0;cursor:pointer;
transition:color .15s,background .15s,transform .12s;
}
.vote button:hover{color:var(--primary);background:var(--primary-l)}
.vote button[aria-pressed="true"]{color:var(--primary);background:var(--primary-l)}
.vote .v-down[aria-pressed="true"]{color:var(--danger);background:#fdecea}
.vote button:active{transform:scale(.88)}
.v-count{font-family:"JetBrains Mono",monospace;font-weight:700;font-size:15px;color:var(--ink);min-width:2ch;text-align:center}
.v-count.bump{animation:bump .32s ease}
@keyframes bump{40%{transform:scale(1.32);color:var(--primary)}}
.accept-mark{margin-top:8px;color:var(--green);display:grid;place-items:center;width:30px;height:30px;border-radius:50%;background:var(--green-soft)}
/* ---------- answers ---------- */
.answers-head{display:flex;flex-wrap:wrap;align-items:center;gap:12px;justify-content:space-between;padding:4px 4px 0}
.answers-head h2{font-size:18px;font-weight:700}
.tabs{display:flex;gap:3px;background:var(--surface-2);padding:3px;border-radius:999px}
.tab{
border:0;background:none;padding:6px 14px;border-radius:999px;
font-size:12.8px;font-weight:600;color:var(--ink-2);cursor:pointer;transition:.15s;
}
.tab:hover{color:var(--ink)}
.tab.on{background:#fff;color:var(--primary-d);box-shadow:var(--sh-1)}
.answers{display:flex;flex-direction:column;gap:16px}
.answer{overflow:hidden;transition:box-shadow .2s,transform .35s cubic-bezier(.2,.7,.3,1),opacity .25s}
.answer:hover{box-shadow:var(--sh-2)}
.answer.moving{opacity:.35;transform:scale(.985)}
.answer.enter{animation:enter .45s cubic-bezier(.2,.8,.3,1)}
@keyframes enter{from{opacity:0;transform:translateY(-14px)}}
.answer.accepted{border-color:rgba(63,163,77,.45);box-shadow:0 0 0 1px rgba(63,163,77,.18),var(--sh-1)}
.solution-banner{
display:flex;align-items:center;gap:8px;
background:linear-gradient(90deg,var(--green-soft),rgba(230,245,232,.35));
color:var(--green-d);font-size:12.5px;font-weight:700;
padding:9px 20px;border-bottom:1px solid rgba(63,163,77,.28);
letter-spacing:.01em;
}
.a-grid{display:grid;grid-template-columns:56px minmax(0,1fr);gap:6px;padding:18px 24px 16px}
.a-body{min-width:0}
.a-foot{display:flex;flex-wrap:wrap;align-items:center;gap:14px;justify-content:space-between;padding-top:12px;border-top:1px solid var(--line)}
.helpful{display:flex;gap:8px;flex-wrap:wrap}
.hbtn{
display:inline-flex;align-items:center;gap:6px;
border:1px solid var(--line);background:#fff;border-radius:999px;
padding:5px 12px;font-size:12.3px;font-weight:600;color:var(--ink-2);cursor:pointer;transition:.15s;
}
.hbtn b{font-family:"JetBrains Mono",monospace}
.hbtn:hover{border-color:var(--primary);color:var(--primary-d);background:var(--primary-l)}
.hbtn.on{border-color:var(--green);background:var(--green-soft);color:var(--green-d)}
.hbtn.on-down{border-color:var(--line-2);background:var(--surface);color:var(--muted)}
.toggle-comments .caret{display:inline-block;transition:transform .2s}
.toggle-comments[aria-expanded="true"] .caret{transform:rotate(90deg)}
.comments{margin-top:12px;border-left:2px solid var(--surface-2);padding-left:14px;display:flex;flex-direction:column;gap:12px}
.comments:not([hidden]){animation:fade .28s ease}
@keyframes fade{from{opacity:0;transform:translateY(-6px)}}
.comment{display:flex;gap:10px;font-size:13px;color:var(--ink-2)}
.comment p{margin:2px 0 0}
.comment .author{font-size:12.5px}
/* ---------- composer ---------- */
.composer{padding:20px 24px 22px}
.composer h2{font-size:17px;font-weight:700;margin-bottom:4px}
.toolbar{display:flex;align-items:center;gap:6px;margin:14px 0 0;padding:6px;background:var(--surface);border:1px solid var(--line);border-bottom:0;border-radius:var(--r-md) var(--r-md) 0 0}
.tbtn{
min-width:32px;height:28px;padding:0 8px;border:1px solid transparent;background:#fff;
border-radius:6px;font-size:12.5px;font-weight:700;cursor:pointer;color:var(--ink-2);
box-shadow:var(--sh-1);transition:.15s;
}
.tbtn.mono{font-family:"JetBrains Mono",monospace}
.tbtn:hover{color:var(--primary-d);border-color:var(--primary)}
.counter{font-family:"JetBrains Mono",monospace;font-size:11.5px;color:var(--muted);padding-right:6px}
.counter.warn{color:var(--danger);font-weight:700}
.composer textarea{
display:block;width:100%;border:1px solid var(--line);border-radius:0 0 var(--r-md) var(--r-md);
padding:14px 16px;font:inherit;font-size:14px;line-height:1.6;resize:vertical;
color:var(--ink);background:#fff;outline:none;
}
.composer textarea:focus{border-color:var(--primary);box-shadow:0 0 0 3px rgba(15,127,168,.14)}
.composer-foot{display:flex;flex-wrap:wrap;gap:12px;align-items:center;justify-content:space-between;margin-top:14px}
/* ---------- sidebar ---------- */
.side{padding:18px 20px}
.side h3{font-size:12px;text-transform:uppercase;letter-spacing:.1em;color:var(--muted);margin-bottom:14px;font-weight:700}
.device{display:flex;gap:12px;align-items:center}
.dev-name{font-weight:700;font-size:14.5px;color:var(--ink)}
.dev-name:hover{color:var(--primary)}
.device p{margin:2px 0 0}
.side-stats{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-top:14px}
.side-stats div{background:var(--surface);border-radius:var(--r-sm);padding:9px 6px;text-align:center}
.side-stats b{display:block;font-family:"JetBrains Mono",monospace;font-size:15px;color:var(--primary-d)}
.side-stats span{font-size:10.5px;color:var(--muted)}
.rel{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}
.rel li{display:flex;gap:10px;align-items:flex-start}
.rel .score{
flex:none;min-width:30px;text-align:center;font-family:"JetBrains Mono",monospace;
font-size:12px;font-weight:700;padding:3px 5px;border-radius:5px;
background:var(--surface);color:var(--ink-2);border:1px solid var(--line);
}
.rel .score[data-solved]{background:var(--green-soft);color:var(--green-d);border-color:rgba(63,163,77,.3)}
.rel a{font-size:13.3px;font-weight:500;color:var(--ink-2);line-height:1.4}
.rel a:hover{color:var(--primary-d)}
.parts{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:4px}
.parts li{display:flex;align-items:center;gap:11px;padding:9px 8px;border-radius:var(--r-sm);transition:background .15s}
.parts li:hover{background:var(--surface)}
.part-thumb{flex:none;width:38px;height:38px;display:grid;place-items:center;background:var(--surface);border-radius:var(--r-sm);border:1px solid var(--line)}
.part-info{display:flex;flex-direction:column;min-width:0;flex:1}
.part-info b{font-size:12.8px;font-weight:600}
.part-info .mono{font-size:10.5px}
.price{font-family:"JetBrains Mono",monospace;font-size:12.5px;font-weight:700;color:var(--ink)}
/* ---------- footer + toast ---------- */
.foot{border-top:1px solid var(--line);background:#fff;margin-top:10px}
.foot-in{display:flex;flex-wrap:wrap;gap:10px;justify-content:space-between;align-items:center;padding-top:20px;padding-bottom:26px;font-size:12px;color:var(--muted)}
.toast-host{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);display:flex;flex-direction:column;gap:8px;z-index:60;pointer-events:none}
.toast{
background:var(--ink);color:#fff;padding:10px 18px;border-radius:999px;
font-size:13px;font-weight:600;box-shadow:var(--sh-2);
animation:toastin .28s cubic-bezier(.2,.8,.3,1);
}
.toast.out{animation:toastout .25s ease forwards}
@keyframes toastin{from{opacity:0;transform:translateY(14px) scale(.96)}}
@keyframes toastout{to{opacity:0;transform:translateY(10px) scale(.97)}}
@media (prefers-reduced-motion:reduce){
*{animation-duration:.001ms !important;transition-duration:.001ms !important}
}
/* ---------- responsive ---------- */
@media (max-width:960px){
.layout{grid-template-columns:minmax(0,1fr)}
.col-side{position:static}
.searchbox input{width:130px}
.topnav{display:none}
}
@media (max-width:520px){
body{font-size:14.5px}
.wrap{padding:0 14px}
.topbar-in{height:56px;gap:10px}
.searchbox{display:none}
.brand-text{font-size:15px}
.question,.a-grid{grid-template-columns:44px minmax(0,1fr);padding:16px 14px 14px;gap:4px}
.question h1{font-size:20px}
.vote button{width:30px;height:26px}
.solution-banner{padding:8px 14px;font-size:11.8px}
.composer{padding:16px 14px 18px}
.side{padding:16px 14px}
.answers-head{padding:0}
.tabs{width:100%;justify-content:space-between}
.tab{flex:1;padding:6px 8px;text-align:center}
.a-foot,.composer-foot{gap:10px}
.side-stats{gap:6px}
.foot-in{padding-bottom:20px}
}/* FIXWELL Answers — thread interactions (vanilla JS, no dependencies) */
(function () {
"use strict";
/* ---------- toast helper ---------- */
var host = document.getElementById("toasts");
function toast(msg) {
if (!host) return;
var el = document.createElement("div");
el.className = "toast";
el.textContent = msg;
host.appendChild(el);
setTimeout(function () {
el.classList.add("out");
setTimeout(function () { el.remove(); }, 260);
}, 2200);
}
document.querySelectorAll("[data-toast]").forEach(function (b) {
b.addEventListener("click", function () { toast(b.getAttribute("data-toast")); });
});
/* ---------- vote controls ---------- */
function wireVote(box) {
var up = box.querySelector(".v-up");
var down = box.querySelector(".v-down");
var out = box.querySelector(".v-count");
var base = parseInt(box.getAttribute("data-count"), 10) || 0;
var state = 0; // -1, 0, 1
function render() {
var n = base + state;
out.textContent = n;
out.classList.remove("bump");
void out.offsetWidth;
out.classList.add("bump");
up.setAttribute("aria-pressed", state === 1 ? "true" : "false");
down.setAttribute("aria-pressed", state === -1 ? "true" : "false");
var card = box.closest(".answer");
if (card) card.setAttribute("data-score", String(n));
}
up.addEventListener("click", function () {
state = state === 1 ? 0 : 1;
render();
toast(state === 1 ? "Upvote recorded." : "Vote removed.");
});
down.addEventListener("click", function () {
state = state === -1 ? 0 : -1;
render();
toast(state === -1 ? "Downvote recorded." : "Vote removed.");
});
}
document.querySelectorAll("[data-vote]").forEach(wireVote);
/* ---------- helpful / not helpful ---------- */
document.addEventListener("click", function (e) {
var btn = e.target.closest("[data-helpful]");
if (!btn) return;
var group = btn.parentElement;
var num = btn.querySelector("b");
var dir = btn.getAttribute("data-helpful");
var active = btn.classList.contains("on") || btn.classList.contains("on-down");
group.querySelectorAll("[data-helpful]").forEach(function (o) {
if (o !== btn && (o.classList.contains("on") || o.classList.contains("on-down"))) {
var n = o.querySelector("b");
n.textContent = Math.max(0, parseInt(n.textContent, 10) - 1);
o.classList.remove("on", "on-down");
}
});
if (active) {
btn.classList.remove("on", "on-down");
num.textContent = Math.max(0, parseInt(num.textContent, 10) - 1);
} else {
btn.classList.add(dir === "up" ? "on" : "on-down");
num.textContent = parseInt(num.textContent, 10) + 1;
toast(dir === "up" ? "Marked as helpful." : "Feedback noted.");
}
});
/* ---------- comment sub-threads ---------- */
document.addEventListener("click", function (e) {
var t = e.target.closest(".toggle-comments");
if (!t) return;
var panel = document.getElementById(t.getAttribute("aria-controls"));
if (!panel) return;
var open = t.getAttribute("aria-expanded") === "true";
t.setAttribute("aria-expanded", open ? "false" : "true");
panel.hidden = open;
});
/* ---------- sort tabs with FLIP reflow ---------- */
var list = document.getElementById("answers");
var tabs = Array.prototype.slice.call(document.querySelectorAll(".tab"));
function sortBy(mode) {
var items = Array.prototype.slice.call(list.children);
// record start positions
var first = new Map();
items.forEach(function (el) { first.set(el, el.getBoundingClientRect().top); });
items.sort(function (a, b) {
if (mode === "helpful") {
var d = (+b.dataset.score) - (+a.dataset.score);
if (d !== 0) return d;
return (+b.dataset.time) - (+a.dataset.time);
}
if (mode === "newest") return (+b.dataset.time) - (+a.dataset.time);
return (+a.dataset.time) - (+b.dataset.time);
});
items.forEach(function (el) { list.appendChild(el); });
// invert + play
items.forEach(function (el) {
var delta = first.get(el) - el.getBoundingClientRect().top;
if (!delta) return;
el.style.transition = "none";
el.style.transform = "translateY(" + delta + "px)";
requestAnimationFrame(function () {
el.style.transition = "transform .42s cubic-bezier(.2,.75,.3,1)";
el.style.transform = "";
});
});
}
tabs.forEach(function (tab) {
tab.addEventListener("click", function () {
tabs.forEach(function (o) { o.classList.remove("on"); o.setAttribute("aria-selected", "false"); });
tab.classList.add("on");
tab.setAttribute("aria-selected", "true");
sortBy(tab.getAttribute("data-sort"));
});
});
/* ---------- composer ---------- */
var ta = document.getElementById("answer-input");
var counter = document.querySelector("[data-counter]");
var postBtn = document.getElementById("post-answer");
var MAX = 1200;
function sync() {
var n = ta.value.length;
counter.textContent = n + " / " + MAX;
counter.classList.toggle("warn", n > MAX - 120);
postBtn.disabled = ta.value.trim().length < 12;
}
ta.addEventListener("input", sync);
sync();
function wrapSelection(before, after, linePrefix) {
var s = ta.selectionStart, e = ta.selectionEnd;
var sel = ta.value.slice(s, e);
var replacement;
if (linePrefix) {
var body = sel || "first step";
replacement = body.split("\n").map(function (l) { return linePrefix + l; }).join("\n");
} else {
replacement = before + (sel || "text") + after;
}
ta.setRangeText(replacement, s, e, "end");
ta.focus();
sync();
}
document.querySelectorAll("[data-fmt]").forEach(function (b) {
b.addEventListener("click", function () {
var f = b.getAttribute("data-fmt");
if (f === "bold") wrapSelection("**", "**");
else if (f === "code") wrapSelection("`", "`");
else wrapSelection("", "", "- ");
});
});
/* ---------- optimistic post ---------- */
var countEl = document.querySelector("[data-answer-count]");
function esc(s) {
return s.replace(/[&<>"']/g, function (c) {
return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c];
});
}
function renderBody(text) {
return esc(text)
.split(/\n{2,}/)
.map(function (block) {
var lines = block.split("\n");
if (lines.every(function (l) { return /^\s*-\s+/.test(l); })) {
return "<ul>" + lines.map(function (l) { return "<li>" + l.replace(/^\s*-\s+/, "") + "</li>"; }).join("") + "</ul>";
}
var html = block
.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
.replace(/`([^`]+)`/g, "<code>$1</code>")
.replace(/\n/g, "<br>");
return "<p>" + html + "</p>";
})
.join("");
}
postBtn.addEventListener("click", function () {
var text = ta.value.trim();
if (text.length < 12) return;
var now = Date.now();
var art = document.createElement("article");
art.className = "card answer enter";
art.setAttribute("data-score", "1");
art.setAttribute("data-time", String(now));
art.innerHTML =
'<div class="a-grid">' +
'<div class="vote" data-vote data-count="1" aria-label="Answer score">' +
'<button class="v-up" type="button" aria-label="Upvote answer" aria-pressed="false"><svg viewBox="0 0 20 20" width="18" height="18" aria-hidden="true"><path d="M10 4.5 17 13H3z" fill="currentColor"/></svg></button>' +
'<span class="v-count">1</span>' +
'<button class="v-down" type="button" aria-label="Downvote answer" aria-pressed="false"><svg viewBox="0 0 20 20" width="18" height="18" aria-hidden="true"><path d="M10 15.5 3 7h14z" fill="currentColor"/></svg></button>' +
"</div>" +
'<div class="a-body">' +
'<div class="meta"><span class="avatar avatar-me" aria-hidden="true">MV</span>' +
'<a href="#" class="author">marek_v</a>' +
'<span class="badge badge-bronze">● 640 rep</span>' +
'<span class="badge badge-flat">Posting…</span>' +
'<span class="sep" aria-hidden="true">·</span><time>just now</time></div>' +
'<div class="prose">' + renderBody(text) + "</div>" +
'<div class="a-foot"><div class="helpful">' +
'<button class="hbtn" type="button" data-helpful="up">Helpful <b>0</b></button>' +
'<button class="hbtn" type="button" data-helpful="down">Not helpful <b>0</b></button>' +
"</div></div>" +
"</div>" +
"</div>";
list.insertBefore(art, list.firstElementChild);
wireVote(art.querySelector("[data-vote]"));
countEl.textContent = list.children.length;
ta.value = "";
sync();
art.scrollIntoView({ behavior: "smooth", block: "center" });
toast("Answer posted — pending community review.");
setTimeout(function () {
var pending = art.querySelector(".badge-flat");
if (pending) { pending.textContent = "New answer"; }
}, 1600);
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>FIXWELL Answers — Laptop shuts off at 40% battery</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip" href="#thread">Skip to thread</a>
<header class="topbar">
<div class="wrap topbar-in">
<a class="brand" href="#" aria-label="FIXWELL Answers home">
<span class="brand-mark" aria-hidden="true">
<svg viewBox="0 0 32 32" width="26" height="26" role="presentation">
<rect x="1" y="1" width="30" height="30" rx="9" fill="#0f7fa8"/>
<path d="M11 21.5 20.5 12a3.2 3.2 0 0 0-4.4-4.6l-1.9 1.9 2.1 2.1-1.6 1.6-2.1-2.1-1.7 1.7 2.1 2.1-1.6 1.6-2.1-2.1-1.4 1.4a3.2 3.2 0 0 0 4.5 4.6z" fill="#fff" opacity=".95"/>
<circle cx="22.5" cy="22.5" r="3.6" fill="#3fa34d"/>
<path d="m20.9 22.6 1.2 1.2 2.2-2.4" stroke="#fff" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</span>
<span class="brand-text">FIXWELL<span class="brand-sub">Answers</span></span>
</a>
<nav class="topnav" aria-label="Primary">
<a href="#thread" class="on">Answers</a>
<a href="#device">Guides</a>
<a href="#parts">Parts</a>
</nav>
<div class="topbar-right">
<div class="searchbox">
<svg viewBox="0 0 20 20" width="15" height="15" aria-hidden="true"><circle cx="8.5" cy="8.5" r="5.5" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="m13 13 4 4" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
<input type="search" placeholder="Search repair answers…" aria-label="Search repair answers" />
</div>
<button class="btn btn-ghost" type="button" data-toast="You are following this thread.">Follow</button>
<span class="avatar avatar-me" aria-label="Signed in as marek_v">MV</span>
</div>
</div>
</header>
<nav class="crumbs wrap" aria-label="Breadcrumb">
<a href="#">Answers</a><span aria-hidden="true">›</span>
<a href="#">Laptops</a><span aria-hidden="true">›</span>
<a href="#device">Nimbus 14 Pro (2019)</a><span aria-hidden="true">›</span>
<span aria-current="page">Power & battery</span>
</nav>
<main class="wrap layout" id="thread">
<div class="col-main">
<!-- QUESTION -->
<article class="card question" aria-labelledby="q-title">
<div class="vote" data-vote data-count="34" aria-label="Question score">
<button class="v-up" type="button" aria-label="Upvote question" aria-pressed="false">
<svg viewBox="0 0 20 20" width="18" height="18" aria-hidden="true"><path d="M10 4.5 17 13H3z" fill="currentColor"/></svg>
</button>
<span class="v-count">34</span>
<button class="v-down" type="button" aria-label="Downvote question" aria-pressed="false">
<svg viewBox="0 0 20 20" width="18" height="18" aria-hidden="true"><path d="M10 15.5 3 7h14z" fill="currentColor"/></svg>
</button>
</div>
<div class="q-body">
<div class="q-top">
<span class="chip chip-device"><span class="dot"></span>Nimbus 14 Pro (2019)</span>
<span class="chip chip-tag">battery</span>
<span class="chip chip-tag">power</span>
<span class="pill pill-solved"><svg viewBox="0 0 16 16" width="12" height="12" aria-hidden="true"><path d="m3.5 8.5 3 3 6-7" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>Solved</span>
</div>
<h1 id="q-title">Laptop shuts off at 40% battery — bad cell or controller?</h1>
<div class="meta">
<span class="avatar" aria-hidden="true">RK</span>
<a href="#" class="author">rk_benchwork</a>
<span class="badge badge-silver" title="Silver contributor">◆ 2,140 rep</span>
<span class="sep" aria-hidden="true">·</span>
<time datetime="2026-07-21T09:12:00Z">asked Jul 21, 2026</time>
<span class="sep" aria-hidden="true">·</span>
<span class="muted">1,284 views</span>
</div>
<div class="prose">
<p>Client brought in a <strong>Nimbus 14 Pro</strong> that hard-powers-off — no shutdown sequence, just instant black — whenever the reported charge drops to about 40%. Plugged into the adapter it runs fine for hours under load, so the board itself seems healthy.</p>
<p>Battery is the original 4-cell 52 Wh pack, roughly 620 cycles. Reported design capacity still shows near-new, which is what makes me suspicious of the gas gauge rather than the cells.</p>
<ul>
<li>Cutoff is repeatable within ±2% across six runs.</li>
<li>No swelling, no heat, no smell from the pack.</li>
<li>Firmware and EC are on the latest release.</li>
</ul>
<p>Is this a single collapsing cell dragging the pack under the protection threshold, or a fuel-gauge IC that has lost its learned capacity table? What do I measure first?</p>
</div>
<figure class="fig" aria-labelledby="fig-cap">
<svg viewBox="0 0 640 220" role="img" aria-label="Chart showing reported battery percentage falling steadily then a sudden cutoff at 40 percent while pack voltage collapses">
<defs>
<linearGradient id="gfill" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#0f7fa8" stop-opacity=".28"/>
<stop offset="1" stop-color="#0f7fa8" stop-opacity="0"/>
</linearGradient>
</defs>
<rect width="640" height="220" fill="#f2f5f7"/>
<g stroke="#d7e0e6" stroke-width="1">
<line x1="52" y1="30" x2="612" y2="30"/><line x1="52" y1="70" x2="612" y2="70"/>
<line x1="52" y1="110" x2="612" y2="110"/><line x1="52" y1="150" x2="612" y2="150"/>
<line x1="52" y1="190" x2="612" y2="190"/>
</g>
<g font-family="JetBrains Mono, monospace" font-size="10" fill="#6a7b86">
<text x="16" y="34">100%</text><text x="22" y="114">60%</text><text x="22" y="154">40%</text><text x="28" y="194">0%</text>
<text x="300" y="212">discharge time →</text>
</g>
<path d="M52 34 L150 52 L250 76 L340 104 L410 132 L452 150 L452 190 L52 190 Z" fill="url(#gfill)"/>
<path d="M52 34 L150 52 L250 76 L340 104 L410 132 L452 150" fill="none" stroke="#0f7fa8" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M452 150 L452 190" fill="none" stroke="#c8442f" stroke-width="3" stroke-dasharray="5 4" stroke-linecap="round"/>
<path d="M52 44 L150 66 L250 96 L340 130 L410 168 L446 186" fill="none" stroke="#8b9aa5" stroke-width="2" stroke-dasharray="3 5"/>
<circle cx="452" cy="150" r="6" fill="#c8442f"/>
<g font-family="JetBrains Mono, monospace" font-size="11">
<text x="466" y="146" fill="#c8442f" font-weight="700">hard cutoff</text>
<text x="466" y="162" fill="#6a7b86">reported 40%</text>
<text x="120" y="30" fill="#0f7fa8" font-weight="700">reported SoC</text>
<text x="120" y="80" fill="#8b9aa5">pack voltage</text>
</g>
</svg>
<figcaption id="fig-cap">Six logged discharge runs overlaid — reported state of charge stays linear while pack voltage sags early and collapses at the protection threshold.</figcaption>
</figure>
<div class="q-actions">
<button class="linkbtn" type="button" data-toast="Link copied to clipboard.">Share</button>
<button class="linkbtn" type="button" data-toast="Saved to your repair bench.">Save</button>
<button class="linkbtn" type="button" data-toast="Thanks — a moderator will review this report.">Report</button>
<span class="grow"></span>
<span class="muted small">Edited Jul 22, 2026 by <a href="#">nadia_cells</a></span>
</div>
</div>
</article>
<!-- ANSWER TOOLBAR -->
<div class="answers-head">
<h2 id="answers-count"><span data-answer-count>4</span> Answers</h2>
<div class="tabs" role="tablist" aria-label="Sort answers">
<button role="tab" class="tab on" aria-selected="true" data-sort="helpful">Most helpful</button>
<button role="tab" class="tab" aria-selected="false" data-sort="newest">Newest</button>
<button role="tab" class="tab" aria-selected="false" data-sort="oldest">Oldest</button>
</div>
</div>
<div class="answers" id="answers">
<!-- ANSWER 1 — accepted -->
<article class="card answer accepted" data-score="58" data-time="1721563920000" aria-labelledby="a1-author">
<div class="solution-banner">
<svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"><path d="m3.5 8.5 3 3 6-7" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
Chosen solution — verified by the asker
</div>
<div class="a-grid">
<div class="vote" data-vote data-count="58" aria-label="Answer score">
<button class="v-up" type="button" aria-label="Upvote answer" aria-pressed="false"><svg viewBox="0 0 20 20" width="18" height="18" aria-hidden="true"><path d="M10 4.5 17 13H3z" fill="currentColor"/></svg></button>
<span class="v-count">58</span>
<button class="v-down" type="button" aria-label="Downvote answer" aria-pressed="false"><svg viewBox="0 0 20 20" width="18" height="18" aria-hidden="true"><path d="M10 15.5 3 7h14z" fill="currentColor"/></svg></button>
<span class="accept-mark" title="Accepted answer" aria-label="Accepted answer">
<svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true"><path d="m4 10.5 4 4 8-9" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/></svg>
</span>
</div>
<div class="a-body">
<div class="meta">
<span class="avatar avatar-gold" aria-hidden="true">NC</span>
<a href="#" class="author" id="a1-author">nadia_cells</a>
<span class="badge badge-gold" title="Gold contributor">★ 18,940 rep</span>
<span class="badge badge-flat">Battery specialist</span>
<span class="sep" aria-hidden="true">·</span>
<time datetime="2026-07-21T13:32:00Z">Jul 21, 2026</time>
</div>
<div class="prose">
<p>It is almost certainly <strong>one collapsing cell</strong>, not the gauge. A lost learning table drifts the reading gradually; a weak cell produces exactly what you have — a hard, repeatable cutoff at the same reported percentage because the protection IC trips on <em>per-cell</em> undervoltage while the pack total still looks fine.</p>
<p>Pull the per-cell voltages from the smart battery over SMBus before you open anything:</p>
<pre class="code"><code># read per-cell voltages from the pack controller
fixwell-batt --bus i2c-3 --addr 0x0b cells
cell 1 4.011 V OK
cell 2 3.987 V OK
cell 3 3.402 V <-- 590 mV below siblings
cell 4 4.004 V OK</code></pre>
<p>A spread over roughly <strong>150 mV</strong> under light load already means the pack is unbalanced. Six hundred millivolts is a dead cell in progress. Repeat the read at 60% and again at 45% — the gap will widen sharply just before the cutoff, which is the fingerprint you want.</p>
<ol>
<li>Log cells at rest, then under a 20 W load.</li>
<li>Note the delta at each step; a growing delta = cell fault.</li>
<li>Flat delta with a jumping percentage = gauge fault.</li>
</ol>
<p>Replace the pack. Re-celling a bonded pack is possible but the spot-welding and the gauge re-learn are rarely worth the bench hour on a 620-cycle battery.</p>
</div>
<div class="a-foot">
<div class="helpful">
<button class="hbtn" type="button" data-helpful="up"><svg viewBox="0 0 16 16" width="13" height="13" aria-hidden="true"><path d="M6 14V7l3-5 1 1-1 3h4l-1.4 8z" fill="currentColor"/></svg> Helpful <b>212</b></button>
<button class="hbtn" type="button" data-helpful="down">Not helpful <b>3</b></button>
</div>
<button class="linkbtn toggle-comments" type="button" aria-expanded="false" aria-controls="c1">
<span class="caret" aria-hidden="true">▸</span> 3 comments
</button>
</div>
<div class="comments" id="c1" hidden>
<div class="comment"><span class="avatar avatar-xs" aria-hidden="true">RK</span><div><a href="#" class="author">rk_benchwork</a> <span class="muted small">Jul 21</span><p>Ran it — cell 3 sat 540 mV low at rest and 780 mV under load. That settles it.</p></div></div>
<div class="comment"><span class="avatar avatar-xs" aria-hidden="true">TM</span><div><a href="#" class="author">torque_mel</a> <span class="muted small">Jul 22</span><p>Worth adding: read at room temperature. A cold pack exaggerates the delta and sends people chasing ghosts.</p></div></div>
<div class="comment"><span class="avatar avatar-xs" aria-hidden="true">NC</span><div><a href="#" class="author">nadia_cells</a> <span class="muted small">Jul 22</span><p>Good catch, added to the checklist. Anything under 15 °C, let it sit an hour first.</p></div></div>
</div>
</div>
</div>
</article>
<!-- ANSWER 2 -->
<article class="card answer" data-score="31" data-time="1721581500000" aria-labelledby="a2-author">
<div class="a-grid">
<div class="vote" data-vote data-count="31" aria-label="Answer score">
<button class="v-up" type="button" aria-label="Upvote answer" aria-pressed="false"><svg viewBox="0 0 20 20" width="18" height="18" aria-hidden="true"><path d="M10 4.5 17 13H3z" fill="currentColor"/></svg></button>
<span class="v-count">31</span>
<button class="v-down" type="button" aria-label="Downvote answer" aria-pressed="false"><svg viewBox="0 0 20 20" width="18" height="18" aria-hidden="true"><path d="M10 15.5 3 7h14z" fill="currentColor"/></svg></button>
</div>
<div class="a-body">
<div class="meta">
<span class="avatar" aria-hidden="true">TM</span>
<a href="#" class="author" id="a2-author">torque_mel</a>
<span class="badge badge-silver" title="Silver contributor">◆ 7,305 rep</span>
<span class="sep" aria-hidden="true">·</span>
<time datetime="2026-07-21T18:25:00Z">Jul 21, 2026</time>
</div>
<div class="prose">
<p>Before you condemn the pack, rule out the cheap failure: the <strong>battery-to-board connector</strong>. On this chassis the flex sits directly under the right speaker and takes a thermal cycle every session. A partially lifted contact adds resistance that only matters once current demand rises at lower charge.</p>
<pre class="code"><code>fixwell-batt --bus i2c-3 esr --samples 40
pack ESR 184 mOhm (expected < 90)
connector ~62 mOhm suspicious
</code></pre>
<p>Reseat the flex, clean both pads with isopropyl, and re-run the discharge. Takes four minutes and saves a pack swap maybe one time in five.</p>
</div>
<div class="a-foot">
<div class="helpful">
<button class="hbtn" type="button" data-helpful="up"><svg viewBox="0 0 16 16" width="13" height="13" aria-hidden="true"><path d="M6 14V7l3-5 1 1-1 3h4l-1.4 8z" fill="currentColor"/></svg> Helpful <b>96</b></button>
<button class="hbtn" type="button" data-helpful="down">Not helpful <b>7</b></button>
</div>
<button class="linkbtn toggle-comments" type="button" aria-expanded="false" aria-controls="c2"><span class="caret" aria-hidden="true">▸</span> 2 comments</button>
</div>
<div class="comments" id="c2" hidden>
<div class="comment"><span class="avatar avatar-xs" aria-hidden="true">JP</span><div><a href="#" class="author">jules_pry</a> <span class="muted small">Jul 22</span><p>Confirmed on two units last month. Both looked like dead packs, both were the flex.</p></div></div>
<div class="comment"><span class="avatar avatar-xs" aria-hidden="true">RK</span><div><a href="#" class="author">rk_benchwork</a> <span class="muted small">Jul 22</span><p>Reseated first — no change here, but I am keeping this in the intake checklist.</p></div></div>
</div>
</div>
</div>
</article>
<!-- ANSWER 3 -->
<article class="card answer" data-score="17" data-time="1721644800000" aria-labelledby="a3-author">
<div class="a-grid">
<div class="vote" data-vote data-count="17" aria-label="Answer score">
<button class="v-up" type="button" aria-label="Upvote answer" aria-pressed="false"><svg viewBox="0 0 20 20" width="18" height="18" aria-hidden="true"><path d="M10 4.5 17 13H3z" fill="currentColor"/></svg></button>
<span class="v-count">17</span>
<button class="v-down" type="button" aria-label="Downvote answer" aria-pressed="false"><svg viewBox="0 0 20 20" width="18" height="18" aria-hidden="true"><path d="M10 15.5 3 7h14z" fill="currentColor"/></svg></button>
</div>
<div class="a-body">
<div class="meta">
<span class="avatar" aria-hidden="true">JP</span>
<a href="#" class="author" id="a3-author">jules_pry</a>
<span class="badge badge-bronze" title="Bronze contributor">● 1,120 rep</span>
<span class="sep" aria-hidden="true">·</span>
<time datetime="2026-07-22T12:00:00Z">Jul 22, 2026</time>
</div>
<div class="prose">
<p>If the per-cell read comes back flat, then it <em>is</em> the gauge and a full calibration cycle is worth one attempt before ordering parts. Charge to 100%, hold two hours, discharge to hardware cutoff with the OS power thresholds disabled, then rest overnight and charge uninterrupted.</p>
<pre class="code"><code>fixwell-batt gauge --reset-learned
fixwell-batt gauge --status
learned capacity 4,812 mAh
design capacity 5,000 mAh
qmax valid no <-- relearn required</code></pre>
<p>One cycle only. If <code>qmax valid</code> is still <code>no</code> afterwards, the gauge cannot relearn and the pack goes in the recycling bin either way.</p>
</div>
<div class="a-foot">
<div class="helpful">
<button class="hbtn" type="button" data-helpful="up"><svg viewBox="0 0 16 16" width="13" height="13" aria-hidden="true"><path d="M6 14V7l3-5 1 1-1 3h4l-1.4 8z" fill="currentColor"/></svg> Helpful <b>54</b></button>
<button class="hbtn" type="button" data-helpful="down">Not helpful <b>9</b></button>
</div>
<button class="linkbtn toggle-comments" type="button" aria-expanded="false" aria-controls="c3"><span class="caret" aria-hidden="true">▸</span> 1 comment</button>
</div>
<div class="comments" id="c3" hidden>
<div class="comment"><span class="avatar avatar-xs" aria-hidden="true">NC</span><div><a href="#" class="author">nadia_cells</a> <span class="muted small">Jul 22</span><p>Fine as a diagnostic, but never bill a customer for a relearn cycle before the per-cell read.</p></div></div>
</div>
</div>
</div>
</article>
<!-- ANSWER 4 -->
<article class="card answer" data-score="6" data-time="1721733000000" aria-labelledby="a4-author">
<div class="a-grid">
<div class="vote" data-vote data-count="6" aria-label="Answer score">
<button class="v-up" type="button" aria-label="Upvote answer" aria-pressed="false"><svg viewBox="0 0 20 20" width="18" height="18" aria-hidden="true"><path d="M10 4.5 17 13H3z" fill="currentColor"/></svg></button>
<span class="v-count">6</span>
<button class="v-down" type="button" aria-label="Downvote answer" aria-pressed="false"><svg viewBox="0 0 20 20" width="18" height="18" aria-hidden="true"><path d="M10 15.5 3 7h14z" fill="currentColor"/></svg></button>
</div>
<div class="a-body">
<div class="meta">
<span class="avatar" aria-hidden="true">SD</span>
<a href="#" class="author" id="a4-author">solder_dana</a>
<span class="badge badge-bronze" title="Bronze contributor">● 480 rep</span>
<span class="sep" aria-hidden="true">·</span>
<time datetime="2026-07-23T08:30:00Z">Jul 23, 2026</time>
</div>
<div class="prose">
<p>Adjacent data point: I saw the identical symptom caused by a hairline crack in the <strong>sense resistor</strong> joint on the daughterboard. Current sensing went intermittent, the gauge integrated garbage, and the EC pulled the plug at a suspiciously round number every time.</p>
<p>Under magnification the joint looked perfect; only a four-wire measurement showed it drifting. Worth twenty seconds with the meter once the pack is exonerated.</p>
</div>
<div class="a-foot">
<div class="helpful">
<button class="hbtn" type="button" data-helpful="up"><svg viewBox="0 0 16 16" width="13" height="13" aria-hidden="true"><path d="M6 14V7l3-5 1 1-1 3h4l-1.4 8z" fill="currentColor"/></svg> Helpful <b>21</b></button>
<button class="hbtn" type="button" data-helpful="down">Not helpful <b>2</b></button>
</div>
<button class="linkbtn toggle-comments" type="button" aria-expanded="false" aria-controls="c4"><span class="caret" aria-hidden="true">▸</span> 1 comment</button>
</div>
<div class="comments" id="c4" hidden>
<div class="comment"><span class="avatar avatar-xs" aria-hidden="true">TM</span><div><a href="#" class="author">torque_mel</a> <span class="muted small">Jul 23</span><p>Rare but real. Four-wire or it did not happen.</p></div></div>
</div>
</div>
</div>
</article>
</div>
<!-- COMPOSER -->
<section class="card composer" aria-labelledby="composer-title">
<h2 id="composer-title">Your answer</h2>
<p class="muted small">Explain what to measure and why. Answers with concrete readings get accepted the most.</p>
<div class="toolbar" role="toolbar" aria-label="Formatting">
<button type="button" class="tbtn" data-fmt="bold" aria-label="Bold"><b>B</b></button>
<button type="button" class="tbtn mono" data-fmt="code" aria-label="Code"></></button>
<button type="button" class="tbtn" data-fmt="list" aria-label="Bulleted list">☰</button>
<span class="grow"></span>
<span class="counter" data-counter>0 / 1200</span>
</div>
<label class="sr-only" for="answer-input">Write your answer</label>
<textarea id="answer-input" maxlength="1200" rows="6" placeholder="Start with the measurement you would take first…"></textarea>
<div class="composer-foot">
<span class="muted small">Posting as <b>marek_v</b> · ● 640 rep</span>
<button class="btn btn-primary" type="button" id="post-answer" disabled>Post answer</button>
</div>
</section>
</div>
<!-- SIDEBAR -->
<aside class="col-side">
<section class="card side" id="device" aria-labelledby="dev-title">
<h3 id="dev-title">Device</h3>
<div class="device">
<svg viewBox="0 0 120 78" width="96" height="62" role="img" aria-label="Line drawing of an open laptop">
<rect x="16" y="8" width="88" height="52" rx="4" fill="#f2f5f7" stroke="#12212b" stroke-width="2.4"/>
<rect x="24" y="16" width="72" height="36" rx="2" fill="#0f7fa8" opacity=".16"/>
<path d="M6 68h108l-6-8H12z" fill="#f2f5f7" stroke="#12212b" stroke-width="2.4" stroke-linejoin="round"/>
<rect x="46" y="62" width="28" height="3" rx="1.5" fill="#12212b" opacity=".45"/>
<rect x="34" y="24" width="24" height="12" rx="2" fill="#3fa34d" opacity=".7"/>
<path d="M62 30h20M62 36h14" stroke="#12212b" stroke-width="2" stroke-linecap="round" opacity=".4"/>
</svg>
<div>
<a href="#" class="dev-name">Nimbus 14 Pro (2019)</a>
<p class="muted small">412 guides · 1,930 answers</p>
</div>
</div>
<div class="side-stats">
<div><b>92%</b><span>repairability</span></div>
<div><b>18</b><span>min teardown</span></div>
<div><b>T5</b><span>driver needed</span></div>
</div>
<a class="btn btn-outline block" href="#">Open device hub</a>
</section>
<section class="card side" aria-labelledby="rel-title">
<h3 id="rel-title">Related questions</h3>
<ul class="rel">
<li><span class="score" data-solved="1">12</span><a href="#">Pack reports 0% after a firmware update — recoverable?</a></li>
<li><span class="score">8</span><a href="#">Charger negotiates 20 V but only delivers 9 V</a></li>
<li><span class="score" data-solved="1">27</span><a href="#">Swollen cell pushed the trackpad up — safe transport?</a></li>
<li><span class="score">5</span><a href="#">Which pins on the pack connector are the thermistor?</a></li>
<li><span class="score" data-solved="1">19</span><a href="#">EC refuses to charge past 80% on a replacement pack</a></li>
</ul>
</section>
<section class="card side" id="parts" aria-labelledby="parts-title">
<h3 id="parts-title">Parts for this fix</h3>
<ul class="parts">
<li>
<span class="part-thumb" aria-hidden="true"><svg viewBox="0 0 40 40" width="30" height="30"><rect x="5" y="12" width="26" height="16" rx="3" fill="#0f7fa8" opacity=".22" stroke="#0f7fa8" stroke-width="2"/><rect x="31" y="17" width="4" height="6" rx="1" fill="#0f7fa8"/></svg></span>
<span class="part-info"><b>52 Wh Replacement Pack</b><span class="mono muted">NB14-BAT-52</span></span>
<span class="price">$74.00</span>
</li>
<li>
<span class="part-thumb" aria-hidden="true"><svg viewBox="0 0 40 40" width="30" height="30"><path d="M8 30 26 12a4 4 0 1 0-6-6L4 24z" fill="#0f7fa8" opacity=".22" stroke="#0f7fa8" stroke-width="2"/></svg></span>
<span class="part-info"><b>Anti-static Spudger Set</b><span class="mono muted">TL-SPD-06</span></span>
<span class="price">$11.50</span>
</li>
<li>
<span class="part-thumb" aria-hidden="true"><svg viewBox="0 0 40 40" width="30" height="30"><circle cx="20" cy="20" r="12" fill="#3fa34d" opacity=".2" stroke="#3fa34d" stroke-width="2"/><path d="M20 12v8l6 3" stroke="#3fa34d" stroke-width="2" fill="none" stroke-linecap="round"/></svg></span>
<span class="part-info"><b>SMBus Bench Probe</b><span class="mono muted">DG-SMB-11</span></span>
<span class="price">$39.00</span>
</li>
</ul>
<button class="btn btn-primary block" type="button" data-toast="3 parts added to your bench cart.">Add all to cart</button>
</section>
</aside>
</main>
<footer class="foot">
<div class="wrap foot-in">
<span class="mono">FIXWELL Answers — fictional repair community</span>
<span class="muted small">Illustrative UI only. Always follow real safety guidance when working with tools, electronics, or repairs.</span>
</div>
</footer>
<div class="toast-host" id="toasts" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Repair Q&A Thread
A single troubleshooting thread for the fictional FIXWELL Answers community. The question card leads with a device chip, tag chips and a “Solved” pill, then the asker’s reputation badge, view count, and a full write-up of the fault — including an inline SVG chart that plots reported state of charge against sagging pack voltage up to the hard cutoff. The vote control on the left is real: up and down toggle, the count animates, and aria-pressed tracks state.
Four answers follow, ranked by score. The accepted one sits at the top behind a green solution banner and a check mark in the vote rail; the rest carry gold, silver and bronze badge tiers, mono command blocks with plausible tool output, and helpful / not-helpful counters that behave like a single-choice group. Each answer hides a comment sub-thread behind a caret toggle wired with aria-expanded and the hidden attribute.
Sort tabs (Most helpful / Newest / Oldest) reorder the list from data-score and data-time and animate the reflow. The composer at the bottom has a live character counter that warns near the limit, a formatting toolbar whose bold / code / list buttons actually wrap the current textarea selection via setRangeText, and a post button that renders the markdown-ish body and prepends the new answer optimistically. The sidebar holds the device hub card, related questions with solved markers, and a parts rail.
How this look is built
The information architecture takes its cue from iFixit Answers-style repair threads, but every technique here is modern CSS. The page is a two-column grid with a position: sticky sidebar instead of the old float-plus-clearfix rail, each card is its own nested grid so the vote rail and body align without absolute positioning, and the answer reflow uses a FLIP pass — measure with getBoundingClientRect(), reappend the nodes, invert with a transform, then release it inside requestAnimationFrame — which animates on the compositor rather than thrashing layout the way animated top offsets used to. All artwork (the discharge chart, the laptop, the part thumbnails, the logo mark) is inline SVG, so there are zero network image requests and everything inherits the palette, and prefers-reduced-motion neutralises the animations in one rule.
Illustrative UI only — always follow real safety guidance when working with tools, electronics, or repairs.