/* Sims4UICheats -- one stylesheet, no external requests.
   Light is the base; dark overrides only the tokens. */

:root{
  --bg:#f6f7fb; --bg-2:#fff; --bg-3:#eef0f7;
  --fg:#171a23; --fg-2:#4a5060; --fg-3:#6b7385;
  --line:#dfe3ee; --line-2:#c9cfe0;
  --accent:#2f6df0; --accent-fg:#fff; --accent-soft:#e7efff;
  --good:#0f7b4d; --good-soft:#e2f5ec;
  --warn:#8a5a00; --warn-soft:#fdf2d8;
  --bad:#b3261e; --bad-soft:#fdeceb;
  --code-bg:#12131a; --code-fg:#e9ecf5;
  /* The code surface is dark in both themes, so its accent does not flip.
     --accent measured 4.03:1 on it in light mode. */
  --code-accent:#8fb0ff;
  --radius:12px; --radius-sm:8px;
  --shadow:0 1px 2px rgba(16,20,40,.06),0 4px 16px rgba(16,20,40,.06);
  --wrap:1120px;
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
  --sans:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --bg:#0e0f15; --bg-2:#171923; --bg-3:#1f2230;
    --fg:#eef0f7; --fg-2:#b6bccd; --fg-3:#8b93a7;
    --line:#272b3a; --line-2:#39405480;
    --accent:#6f9bff; --accent-fg:#0b0d13; --accent-soft:#1a2540;
    --good:#4fd39a; --good-soft:#12291f;
    --warn:#e8b757; --warn-soft:#2b2312;
    --bad:#ff7b72; --bad-soft:#2c1614;
    --code-bg:#0a0b10; --code-fg:#e9ecf5;
    --shadow:0 1px 2px rgba(0,0,0,.4),0 4px 16px rgba(0,0,0,.3);
  }
}
:root[data-theme="dark"]{
  --bg:#0e0f15; --bg-2:#171923; --bg-3:#1f2230;
  --fg:#eef0f7; --fg-2:#b6bccd; --fg-3:#8b93a7;
  --line:#272b3a; --line-2:#394054;
  --accent:#6f9bff; --accent-fg:#0b0d13; --accent-soft:#1a2540;
  --good:#4fd39a; --good-soft:#12291f;
  --warn:#e8b757; --warn-soft:#2b2312;
  --bad:#ff7b72; --bad-soft:#2c1614;
  --code-bg:#0a0b10; --code-fg:#e9ecf5;
  --shadow:0 1px 2px rgba(0,0,0,.4),0 4px 16px rgba(0,0,0,.3);
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--bg);color:var(--fg);
  font:400 16px/1.62 var(--sans);
  text-rendering:optimizeLegibility;
  overflow-x:hidden;
}
img{max-width:100%;height:auto}
/* Prose carries long game identifiers such as
   SimRelationship.get_track_relationship_data, which at 375px pushed the
   whole page sideways. These are references, not commands to retype, so
   they may break; the rules for .cheat code and table.data code, where a
   break would corrupt something the reader is about to type, are stricter
   and are set separately below. */
code{overflow-wrap:anywhere}
.cheat code,table.data code,code[data-cheat]{overflow-wrap:normal}
a{color:var(--accent);text-underline-offset:2px}
a:hover{text-decoration:underline}
h1,h2,h3,h4{line-height:1.22;margin:0 0 .5em;font-weight:700;letter-spacing:-.011em}
h1{font-size:clamp(1.7rem,1.2rem + 2vw,2.5rem);margin-bottom:.35em}
h2{font-size:clamp(1.3rem,1.1rem + .8vw,1.65rem);margin-top:2.2rem;padding-top:.2rem}
h3{font-size:1.1rem;margin-top:1.6rem}
p,ul,ol{margin:0 0 1rem}
li{margin:.25rem 0}
.wrap{width:100%;max-width:var(--wrap);margin-inline:auto;padding-inline:20px}
/* 1120px everywhere meant a 1876px monitor rendered an 820px article and
   scrolled a 949px table sideways with 700px of screen unused. */
@media (min-width:1400px){:root{--wrap:1320px}}
@media (min-width:1700px){:root{--wrap:1440px}}
.small{font-size:.875rem}
.muted{color:var(--fg-3)}
.skip-link{position:absolute;left:-9999px;top:0;background:var(--accent);color:var(--accent-fg);padding:.6rem 1rem;z-index:100}
.skip-link:focus{left:0}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}

/* ---------- header ---------- */
.hdr{position:sticky;top:0;z-index:40;background:color-mix(in srgb,var(--bg) 88%,transparent);backdrop-filter:blur(10px);border-bottom:1px solid var(--line)}
.hdr-inner{display:flex;align-items:center;gap:1rem;height:60px}
.brand{display:inline-flex;align-items:center;gap:.55rem;font-weight:700;color:var(--fg);text-decoration:none;font-size:1.02rem;white-space:nowrap}
.brand b{color:var(--accent);font-weight:700}
.brand-mark{display:block;width:28px;height:28px;flex:none;color:var(--accent)}
.nav{display:flex;gap:.15rem;margin-left:.5rem;flex:1;min-width:0}
.nav a{padding:.45rem .6rem;border-radius:var(--radius-sm);color:var(--fg-2);text-decoration:none;font-size:.925rem;font-weight:500;white-space:nowrap}
.nav a:hover{background:var(--bg-3);color:var(--fg)}
.nav a[aria-current="page"]{color:var(--accent);background:var(--accent-soft)}
.hdr-tools{display:flex;align-items:center;gap:.4rem;margin-left:auto}
.search-trigger{display:inline-flex;align-items:center;gap:.5rem;background:var(--bg-2);border:1px solid var(--line);color:var(--fg-3);border-radius:999px;padding:.4rem .7rem;font:inherit;font-size:.875rem;cursor:pointer}
.search-trigger:hover{border-color:var(--line-2);color:var(--fg)}
.search-trigger kbd{font:600 .72rem/1 var(--mono);border:1px solid var(--line);border-radius:5px;padding:.22rem .34rem;color:var(--fg-3);background:var(--bg-3)}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:.45rem;border-radius:var(--radius-sm);border:1px solid var(--line);background:var(--bg-2);color:var(--fg);padding:.5rem .85rem;font:600 .9rem/1 var(--sans);cursor:pointer;text-decoration:none}
.btn:hover{border-color:var(--line-2);text-decoration:none}
.btn-primary{background:var(--accent);border-color:var(--accent);color:var(--accent-fg)}
.btn-ghost{background:transparent}
.btn-icon{width:34px;height:34px;padding:0}
.nav-toggle{display:none}
@media (max-width:900px){
  .nav{position:absolute;left:0;right:0;top:60px;flex-direction:column;background:var(--bg-2);border-bottom:1px solid var(--line);padding:.5rem;display:none}
  .nav.open{display:flex}
  .nav-toggle{display:inline-flex}
  .search-trigger span{display:none}
}

/* ---------- page furniture ---------- */
/* padding-BLOCK, not the shorthand. Every page is <div class="wrap page">,
   and `padding:1.6rem 0 3rem` on .page came after .wrap at equal
   specificity, so it reset .wrap's 20px inline padding to zero. Above
   1120px the max-width hid it; on a phone every heading, paragraph and
   card sat flush against both screen edges. */
.page{padding-block:1.6rem 3rem}
.crumbs{font-size:.85rem;color:var(--fg-3);margin-bottom:.9rem}
.crumbs ol{list-style:none;display:flex;flex-wrap:wrap;gap:.35rem;margin:0;padding:0}
.crumbs li::after{content:"/";margin-left:.35rem;color:var(--line-2)}
.crumbs li:last-child::after{content:""}
.layout{display:grid;grid-template-columns:minmax(0,1fr) 260px;gap:2.4rem;align-items:start}
@media (max-width:1000px){.layout{grid-template-columns:minmax(0,1fr)}.side{display:none}}
.side{position:sticky;top:76px;font-size:.9rem}
.side h4{font-size:.72rem;text-transform:uppercase;letter-spacing:.07em;color:var(--fg-3);margin:0 0 .5rem}
.side ul{list-style:none;margin:0 0 1.4rem;padding:0;border-left:2px solid var(--line)}
.side li a{display:block;padding:.28rem .7rem;color:var(--fg-2);text-decoration:none;border-radius:0 6px 6px 0}
.side li a:hover{background:var(--bg-3);color:var(--fg);text-decoration:none}

/* ---------- the answer block: the whole point of the page ---------- */
.answer{
  background:var(--bg-2);border:1px solid var(--line);border-left:4px solid var(--accent);
  border-radius:var(--radius);padding:1.15rem 1.25rem;margin:0 0 1.5rem;
  font-size:1.075rem;line-height:1.55;box-shadow:var(--shadow);
}
.answer strong{font-weight:700}
.answer code{background:var(--code-bg);color:var(--code-fg);padding:.14em .42em;border-radius:5px;font:600 .92em/1 var(--mono)}
.answer p:last-child{margin-bottom:0}
.stamp{display:inline-flex;align-items:center;gap:.4rem;font-size:.78rem;color:var(--fg-3);margin-top:.7rem}
.stamp b{color:var(--good);font-weight:600}

/* ---------- cheat copy component ---------- */
.cheat{
  display:flex;align-items:center;gap:.75rem;flex-wrap:wrap;
  background:var(--code-bg);border-radius:var(--radius-sm);
  padding:.7rem .8rem;margin:.6rem 0;
}
/* A command never breaks across lines: a wrapped command reads as two
   commands, and it is the one string on the page a reader retypes. If the
   box is too narrow the code scrolls inside it instead. */
.cheat code{color:var(--code-fg);font:600 1rem/1.4 var(--mono);
  white-space:nowrap;word-break:normal;flex:1 1 auto;min-width:0;
  overflow-x:auto;scrollbar-width:none}
.cheat code::-webkit-scrollbar{display:none}
.cheat-note{color:#9aa3bb;font-size:.84rem}
.cheat button{
  background:#ffffff1a;border:1px solid #ffffff2e;color:#e9ecf5;
  border-radius:6px;padding:.34rem .6rem;font:600 .8rem/1 var(--sans);cursor:pointer;flex:none;
}
.cheat button:hover{background:#ffffff2b}
.cheat button[data-copied]{background:var(--good);border-color:var(--good);color:#04140c}

/* ---------- tables ---------- */
.table-wrap{margin:1rem 0}
.scroll-x{overflow-x:auto;-webkit-overflow-scrolling:touch;border:1px solid var(--line);border-radius:var(--radius);background:var(--bg-2);
  overscroll-behavior-x:contain;scrollbar-width:thin;
  scrollbar-color:var(--line-2) var(--bg-3)}
/* The scrollbar is permanent rather than hover-revealed, and the right edge
   fades while there is more table to reach. Without both, a column clipped
   mid-string reads as a broken layout instead of a scrollable one. */
.scroll-x::-webkit-scrollbar{height:10px}
.scroll-x::-webkit-scrollbar-track{background:var(--bg-3);border-radius:0 0 var(--radius) var(--radius)}
.scroll-x::-webkit-scrollbar-thumb{background:var(--line-2);border-radius:6px}
.scroll-x{
  background-image:
    linear-gradient(to right, var(--bg-2), transparent 30px),
    linear-gradient(to left,  var(--bg-2), transparent 30px),
    linear-gradient(to right, rgba(0,0,0,.22), transparent 14px),
    linear-gradient(to left,  rgba(0,0,0,.22), transparent 14px);
  background-position:left center,right center,left center,right center;
  background-repeat:no-repeat;
  background-size:34px 100%,34px 100%,16px 100%,16px 100%;
  /* local = pinned to the content, so the cue disappears at each end;
     scroll = pinned to the box, so the shadow only shows while there is
     something under it. */
  background-attachment:local,local,scroll,scroll;
}
table.data{border-collapse:collapse;width:100%;font-size:.92rem;min-width:520px}
table.data th,table.data td{padding:.55rem .6rem;text-align:left;
  border-bottom:1px solid var(--line);vertical-align:top}
/* Auto layout distributes by content heuristics and gets it backwards on
   these tables: the Command column took 363px to show a ten-character
   command while the sentence explaining it was squeezed into 119px and ran
   to four lines. A cell holding one token shrinks to that token; the prose
   columns take the slack. */
table.data td:has(> .cheat),
table.data td:has(> code:only-child){width:1%;white-space:nowrap}
/* Seven columns with two unbreakable tokens in them will not fit any
   article width, so the table scrolls -- and the command column travels
   with it. Scrolling right to read a registry line is useless if the
   command it belongs to has slid off the left edge. */
table.data th:first-child,
table.data td:first-child{position:sticky;left:0;z-index:1;
  background:var(--bg-2);box-shadow:1px 0 0 var(--line)}
table.data thead th:first-child{background:var(--bg-3);z-index:3}
table.data tbody tr:hover td:first-child{background:var(--bg-3)}
table.data thead th{position:sticky;top:0;background:var(--bg-3);font-size:.78rem;text-transform:uppercase;letter-spacing:.05em;color:var(--fg-3);white-space:nowrap;z-index:1}
table.data tbody tr:last-child td{border-bottom:0}
table.data tbody tr:hover{background:var(--bg-3)}
table.data td.num{text-align:right;font-variant-numeric:tabular-nums}
table.data code{font:600 .82em/1 var(--mono);background:var(--bg-3);
  padding:.16em .4em;border-radius:5px;color:var(--fg);white-space:nowrap}
/* Registry citations and pack codes are single tokens; only prose columns
   wrap. td.wrap opts a cell back into wrapping where a page needs it. */
/* Auto layout gets prose-heavy tables badly wrong -- on the panel table it
   handed 1040px to a column of em-dashes and squeezed the column carrying
   every sentence into 165px. Where a table declares its own <colgroup>, the
   proportions are the author's and the browser does not get a vote. */
table.data.cols{table-layout:fixed}
table.data.cols td{overflow-wrap:break-word}
table.data td.nowrap{white-space:nowrap}
table.data td.wrap{white-space:normal}
/* In a cell the copy component is a chip, not a block: the wrapping flex
   row was dropping the button onto its own line and making every row four
   lines tall. */
table.data .cheat{display:inline-flex;flex-wrap:nowrap;margin:0;gap:.45rem;
  padding:.26rem .32rem .26rem .5rem;background:var(--bg-3);max-width:100%}
table.data .cheat code{background:none;padding:0;font-size:.9rem;
  color:var(--fg);flex:0 1 auto}
table.data .cheat button{padding:.18rem .42rem;font-size:.72rem;
  background:var(--bg-2);border-color:var(--line-2);color:var(--fg-2)}
table.data .cheat button:hover{background:var(--accent-soft);color:var(--fg)}

/* Below 720px a data table stops being a table. Sideways scrolling puts the
   command in column one and the answer in column five, so the two are never
   on screen at the same time -- which is the one thing a phone reader needs.
   Each row becomes a card, and every cell names its own column from the
   data-label the build stamped on it. */
@media (max-width:719px){
  table.data.stack{min-width:0;table-layout:auto;display:block;font-size:.95rem}
  table.data.stack thead{display:none}
  table.data.stack tbody,table.data.stack tr,table.data.stack td{display:block;width:auto}
  table.data.stack colgroup{display:none}
  table.data.stack tr{padding:.85rem .9rem;border-bottom:1px solid var(--line)}
  table.data.stack tr:last-child{border-bottom:0}
  /* Each cell scrolls on its own rather than the table doing it. A command
     still never breaks across lines -- a wrapped command reads as two
     commands -- but an unbreakable 38-character registry citation can no
     longer drag the whole page sideways, which it did on 13 of 26 pages. */
  table.data.stack td{border:0;padding:.1rem 0;position:static;background:none;
    box-shadow:none;white-space:normal;max-width:100%;
    overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:none}
  table.data.stack td::-webkit-scrollbar{height:0}
  table.data.stack td:first-child{font-size:1.02rem;font-weight:700;
    margin-bottom:.35rem}
  table.data.stack td[data-label]::before{content:attr(data-label);display:block;
    font:700 .68rem/1.5 var(--sans);text-transform:uppercase;letter-spacing:.07em;
    color:var(--fg-3)}
  table.data.stack td[data-label]{margin-top:.5rem}
  table.data.stack td:empty{display:none}
  /* key/value tables carry their header in a row-leading <th> instead */
  table.data.stack tbody th{display:block;width:auto;border:0;padding:.1rem 0;
    text-align:left;font:700 .68rem/1.5 var(--sans);text-transform:uppercase;
    letter-spacing:.07em;color:var(--fg-3);position:static;background:none}
  /* the container has nothing left to scroll, so drop the scroll furniture */
  .scroll-x:has(table.data.stack){overflow-x:visible}
  .scroll-x:has(table.data.stack){background-image:none}
}

/* Classes rather than style attributes, because public/_headers ships
   style-src 'self' and an inline style is simply dropped at the edge. */
.is-ok{color:var(--good)}
.is-no{color:var(--warn)}
.toc .label{margin-bottom:.6rem}
.ftr-brand{margin-bottom:.75rem}
.ftr-blurb{max-width:30ch}
/* Column proportions for table.data.cols. Percentages have to reach the
   <col>, and a style attribute cannot, so each width in use gets a class. */
col.w10{width:10%}col.w15{width:15%}col.w16{width:16%}col.w17{width:17%}
col.w19{width:19%}col.w24{width:24%}col.w26{width:26%}col.w40{width:40%}
col.w50{width:50%}col.w57{width:57%}

/* ---------- figures ---------- */
/* Figures are inlined SVG, not <img>: they take their colour from the tokens
   above so they follow the reader's theme, they cost no second request in
   front of the largest paint, and the CSP forbids the style attributes an
   externally-referenced SVG would need to theme itself. Every colour and
   every font below is a class, for the same reason. */
.fig{margin:1.5rem 0;padding:0}
/* Authored at 360px, so a 375px phone renders these at 0.93x and the type
   lands where it was designed. Uncapped, a 1318px column scaled the same
   type to 3x; 470 holds it just above its authored size on a desktop. */
.fig svg,.figsvg{display:block;width:100%;max-width:470px;height:auto;
  margin-inline:auto}
.fig figcaption{font-size:.85rem;color:var(--fg-3);margin-top:.6rem;
  max-width:470px;margin-inline:auto;text-align:left}
.fig figcaption b{color:var(--fg-2);font-weight:600}

.fig-bg{fill:var(--bg-3)}
/* 1.14:1 against the figure ground in light mode, so the card was invisible
   without an edge. */
.fig-panel{fill:var(--bg-2);stroke:var(--line-2);stroke-width:1}
.fig-frame{fill:none;stroke:var(--line-2);stroke-width:1}
.fig-track{fill:var(--line)}
.fig-accent{fill:var(--accent)}
.fig-good{fill:var(--good)}
.fig-good-bar{fill:var(--good)}
.fig-step{fill:var(--line-2)}
.fig-mute{fill:var(--fg-3);opacity:.55}
.fig-mute2{fill:var(--fg-3);opacity:.3}
.fig-goodfill{fill:var(--good-soft);stroke:var(--good);stroke-width:1}
.fig-warnfill{fill:var(--warn-soft);stroke:var(--warn);stroke-width:1}
.fig-warnbox{fill:var(--warn-soft)}
.fig-tipbox{fill:var(--accent-soft)}
.fig-arrow{stroke:var(--fg-3);stroke-width:1.5;fill:none}
.fig-arrowhead{fill:var(--fg-3)}
.fig-brace{stroke:var(--accent);stroke-width:1.5;fill:none}
.fig-tree{stroke:var(--line-2);stroke-width:1.5;fill:none}

.fig-t{fill:var(--fg);font:600 16px/1 var(--sans)}
.fig-t2{fill:var(--fg-2);font:400 13.5px/1 var(--sans)}
/* --fg-3 measured 4.18:1 on the figure ground in light mode, under AA for
   text this size. The label class keeps it: bold at 14px it counts as
   large text and passes at 3:1. */
.fig-t3{fill:var(--fg-2);font:400 12px/1 var(--sans)}
/* 4.18:1 on the figure ground in light mode, and at 11px it is nowhere near
   the large-text exemption, so it takes the same token as the body text. */
.fig-lab{fill:var(--fg-2);font:700 11px/1 var(--sans);letter-spacing:.08em}
.fig-mono{fill:var(--fg);font:600 12.5px/1 var(--mono)}
.fig-num-sm{fill:var(--fg);font:700 12.5px/1 var(--sans);
  font-variant-numeric:tabular-nums}
.fig-onbar{fill:var(--accent-fg);font:700 12px/1 var(--sans)}
.fig-ok{fill:var(--good);font:600 11.5px/1 var(--sans)}
.fig-no{fill:var(--warn);font:600 11.5px/1 var(--sans)}

.fig-key{fill:var(--bg-2);stroke:var(--line-2);stroke-width:1}
.fig-keytop{fill:var(--accent);opacity:.85}
.fig-keytext{fill:var(--fg);font:600 16px/1 var(--sans)}
.fig-console{fill:var(--code-bg)}
.fig-consolecaret{fill:var(--code-accent);font:700 14px/1 var(--mono)}
.fig-consoletext{fill:var(--code-fg);font:600 13px/1 var(--mono)}

.fig-tok-cmd{fill:var(--accent)}
.fig-tok-req{fill:var(--bg-2);stroke:var(--accent);stroke-width:1.5}
.fig-tok-opt{fill:var(--bg-2);stroke:var(--line-2);stroke-width:1.5;
  stroke-dasharray:4 3}
.fig-tokentext{fill:var(--fg);font:600 12.5px/1 var(--mono)}
.fig-tokentext-on{fill:var(--accent-fg);font:700 12.5px/1 var(--mono)}

/* ---------- notes ---------- */
.note{display:flex;gap:.7rem;border:1px solid var(--line);border-radius:var(--radius);padding:.85rem 1rem;margin:1.1rem 0;background:var(--bg-2)}
.note .note-title{font-weight:700;margin-bottom:.15rem}
.note p:last-child{margin-bottom:0}
.note-tip{background:var(--accent-soft);border-color:transparent}
.note-warn{background:var(--warn-soft);border-color:transparent}
.note-key{background:var(--good-soft);border-color:transparent}

/* ---------- mod card: credit and outbound download ---------- */
.modcard{border:1px solid var(--line);border-radius:var(--radius);background:var(--bg-2);padding:1.1rem 1.2rem;margin:1.2rem 0;box-shadow:var(--shadow)}
.modcard .by{font-size:.9rem;color:var(--fg-2);margin-bottom:.8rem}
.modcard .by a{font-weight:600}
.modcard .row{display:flex;flex-wrap:wrap;gap:.55rem;align-items:center}
.modcard .meta{display:flex;flex-wrap:wrap;gap:.4rem 1.1rem;font-size:.84rem;color:var(--fg-3);margin-top:.8rem}
.modcard .meta b{color:var(--fg-2);font-weight:600}
.ext::after{content:"\2197";font-size:.8em;margin-left:.3em;opacity:.75}

/* ---------- home page: the mod presentation ---------- */
/* Four numbers, and each one is a claim the page then substantiates. They sit
   on their own row rather than inside prose because they are the fastest
   answer to "is this thing worth installing". */
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(155px,1fr));
  gap:.6rem;margin:1.2rem 0 1.6rem;padding:0}
.kpis>div{border:1px solid var(--line);border-radius:var(--radius);
  background:var(--bg-2);padding:.8rem .9rem}
.kpis b{display:block;font:700 1.6rem/1.1 var(--sans);color:var(--accent);
  font-variant-numeric:tabular-nums;letter-spacing:-.02em}
.kpis span{display:block;font-size:.82rem;color:var(--fg-3);margin-top:.2rem}

/* Two tiles, because the mod is two files and which one failed is the whole
   diagnosis. Side by side above 640px so the pairing is visible at a glance. */
.split{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));
  gap:.75rem;margin:1.1rem 0}
.tile{border:1px solid var(--line);border-radius:var(--radius);
  background:var(--bg-2);padding:.9rem 1rem}
.tile-tag{font-size:.72rem;font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;color:var(--accent)}
.tile>code{display:block;font:600 .9rem/1.4 var(--mono);color:var(--fg);
  margin:.3rem 0 .5rem;overflow-wrap:anywhere}
.tile p{margin:0;font-size:.92rem;color:var(--fg-2)}

/* Numbered because the order is load-bearing: deleting the old copy after
   installing the new one is one of the documented ways to break it. */
.steps{counter-reset:s;list-style:none;padding:0;margin:1.1rem 0}
.steps li{counter-increment:s;position:relative;padding:.15rem 0 .9rem 2.4rem;
  border-left:2px solid var(--line);margin:0 0 0 .85rem}
.steps li:last-child{border-left-color:transparent;padding-bottom:0}
.steps li::before{content:counter(s);position:absolute;left:-.85rem;top:0;
  width:1.7rem;height:1.7rem;border-radius:50%;background:var(--accent);
  color:var(--accent-fg);font:700 .82rem/1.7rem var(--sans);text-align:center}

/* ---------- related strip ---------- */
.related{border-top:1px solid var(--line);margin-top:2.5rem;padding-top:1.4rem}
.related h2{margin-top:0;font-size:1.05rem}
.related ul{list-style:none;padding:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:.5rem}
.related a{display:block;padding:.6rem .75rem;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--bg-2);text-decoration:none;color:var(--fg);font-size:.9rem;font-weight:500}
.related a:hover{border-color:var(--accent);text-decoration:none}
.related .sec{display:block;font-size:.78rem;font-weight:400;color:var(--fg-3)}

/* ---------- faq ---------- */
.faq{margin-top:2.2rem}
.faq details{border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--bg-2);margin:.5rem 0;padding:.2rem .9rem}
.faq summary{cursor:pointer;font-weight:600;padding:.65rem 0;list-style:none}
.faq summary::-webkit-details-marker{display:none}
.faq summary::before{content:"+";display:inline-block;width:1.1em;color:var(--accent);font-weight:700}
.faq details[open] summary::before{content:"\2013"}
.faq details p{margin:0 0 .8rem}

/* ---------- footer ---------- */
.ftr{border-top:1px solid var(--line);background:var(--bg-2);margin-top:3rem;padding:2.4rem 0 1.6rem}
.ftr-grid{display:grid;grid-template-columns:1.4fr repeat(4,1fr);gap:1.6rem}
@media (max-width:860px){.ftr-grid{grid-template-columns:repeat(2,1fr)}}
.ftr h3{font-size:.75rem;text-transform:uppercase;letter-spacing:.07em;color:var(--fg-3);margin:0 0 .6rem}
.ftr ul{list-style:none;margin:0;padding:0;font-size:.9rem}
.ftr ul a{color:var(--fg-2);text-decoration:none}
.ftr ul a:hover{color:var(--accent)}
.ftr-legal{border-top:1px solid var(--line);margin-top:1.8rem;padding-top:1.1rem;font-size:.8rem;color:var(--fg-3)}
.ftr-legal p{margin:.3rem 0}

/* ---------- search overlay ---------- */
.search-modal{position:fixed;inset:0;z-index:60;display:none;padding:8vh 20px 20px;background:rgba(6,8,14,.55)}
.search-modal.open{display:block}
.search-box{max-width:640px;margin-inline:auto;background:var(--bg-2);border:1px solid var(--line);border-radius:var(--radius);box-shadow:0 24px 60px rgba(0,0,0,.35);overflow:hidden}
.search-box input{width:100%;border:0;border-bottom:1px solid var(--line);background:transparent;color:var(--fg);padding:1rem 1.1rem;font:500 1.05rem/1 var(--sans)}
.search-box input:focus{outline:none}
.search-results{max-height:56vh;overflow:auto;margin:0;padding:.35rem;list-style:none}
.search-results a{display:block;padding:.55rem .75rem;border-radius:var(--radius-sm);color:var(--fg);text-decoration:none}
.search-results a:hover,.search-results li[aria-selected="true"] a{background:var(--accent-soft)}
.search-results .sec{display:block;font-size:.76rem;color:var(--fg-3)}

@media print{.hdr,.ftr,.side,.related,.search-modal{display:none}}

/* Command cells in tables are copy targets too (app.js tags the Command
   column). Signalled with a pointer and a hover tint rather than a button per
   row, which would bury the data under chrome. */
table[data-sortable] thead th{cursor:pointer}
code[data-cheat]{cursor:pointer;border-radius:4px;padding:.1em .3em;
  background:var(--bg-3);transition:background .12s}
code[data-cheat]:hover{background:var(--accent-soft)}
code[data-cheat]:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
code[data-cheat][data-copied]{background:var(--good);color:#04140c}
code[data-cheat][data-copied]::after{content:" copied";font-size:.82em;opacity:.85}
