body {
  min-height: 100vh;
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font:
    16px/1.5 "Segoe UI",
    system-ui,
    sans-serif;
}

/* The site rail, page header, and footer build stamp are shared/chrome.css,
   linked in index.html right after tokens.css. Only this app's own rules
   (including the extra focus outline below) live here. */
button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.wrap {
  width: min(var(--page-width), 100%);
  margin: 0 auto;
  /* .pagehead owns the title's top spacing. Keeping the shell flush here
     matches Prism Crafting and avoids a second, app-specific top gutter. */
  padding: 0 20px 28px;
}

.atlas-layout {
  display: grid;
  gap: 42px;
}

@media (min-width: 1080px) {
  .atlas-layout {
    /* Sized as a share of the row rather than capped at a fixed width: at a
       520px cap the upgrade column could not hold "Kullean Transfiguration"
       or "Masterworking" on one line. The two minimums are what keeps the
       share honest just past the breakpoint, where a fixed cap would take
       the whole row and leave the outcome pool unreadable. */
    grid-template-columns: minmax(500px, 0.62fr) minmax(400px, 1fr);
    align-items: start;
  }

  /* The checklist column is still narrower here than in the single-column
     layout, so it keeps its own (smaller) track minimums — the full-width
     220px status minimum left almost nothing for the title column and
     forced it to overflow instead of wrap. */
  .checklist-columns {
    grid-template-columns: minmax(0, 1fr) minmax(170px, 0.8fr);
    gap: 12px;
    padding-left: calc(92px + var(--panel-gutter));
  }

  .task {
    grid-template-columns: 28px 30px minmax(0, 1fr) minmax(170px, 0.8fr);
    gap: 12px;
  }
}

/* Frameless, like Prism Crafting's card grid: the outer panel this used to be
   put a border and a shadow around a set of cards that already carry their own,
   and its padding cost the narrow half of .atlas-layout ~48px of the width the
   cards need to keep two columns. */
.atlas-section {
  min-width: 0;
}

/* Every note above the cards is one of these: a plain disclosure whose summary
   is the whole line a reader sees until they ask for more, matching Prism
   Crafting's .odds-note rather than this page's older gold callout. */
.pool-notes {
  display: grid;
  gap: 6px;
  margin: 0;
  max-width: 70ch;
}

.pool-note > summary {
  display: list-item;
  width: fit-content;
  color: var(--muted);
  cursor: pointer;
  font-size: var(--fs-sm);
}

.pool-note > summary:hover {
  color: var(--accent-ink);
}

.pool-note > summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.scope-note {
  display: grid;
  gap: 6px;
  margin: 6px 0 0;
  padding: 9px 11px;
  border: 1px solid var(--edge-soft);
  border-radius: var(--radius-sm);
  background: var(--panel);
}

.scope-note p {
  margin: 0;
  color: var(--ink);
  font-size: var(--fs-sm);
  line-height: 1.45;
}

.outcome-share-breakdown {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  margin: 0 0 4px;
}

.outcome-share-breakdown span {
  display: grid;
  gap: 2px;
  padding: 8px 10px;
  border: 1px solid var(--edge-soft);
  border-radius: var(--radius-md);
  background: rgb(8 12 17 / 34%);
  color: var(--faint);
  font-size: var(--fs-xs);
}

.outcome-share-breakdown strong {
  color: var(--accent-ink);
  font-size: var(--fs-lg);
  font-variant-numeric: tabular-nums;
}

.outcome-share-breakdown small {
  color: var(--muted);
  font-size: var(--fs-2xs);
}

/* One column per family, side by side rather than in one tall stack: the
   families are independent pools, and stacked they pushed the class affixes —
   the ones a reader picked a class to see — below the fold. Each is a panel card
   matching Prism Crafting's .pcard so the two pages read as one site.

   auto-fit, not repeat(6) like Prism Crafting: this column is the narrow half of
   .atlas-layout above 1080px, so it is ~460px wide on a 1080px screen and ~780px
   on a 1400px one. Held at a fixed count the narrow case wrapped every affix
   name onto three lines and collided the separate-outcome ranges with their info
   icons. The 230px floor is what a name plus its figure needs, so the count
   falls to two and then one on its own as the column narrows. */
.affix-results {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  align-items: start;
  gap: 10px;
  margin-top: 18px;
}

/* .pcard, to the pixel. */
.outcome-family {
  min-width: 0;
  padding: 12px 12px 11px;
  border: 1px solid var(--edge-soft);
  border-radius: var(--radius-md);
  background: var(--panel);
}

/* No rule under the title, the way a prism card has none: the chips below carry
   their own borders, so a divider between them and the heading is a third line
   in a card that already has two. */
.outcome-family-heading {
  margin: 0 0 2px;
}

.outcome-family-heading h3 {
  margin: 0;
  color: var(--accent-ink);
  font-family: "Palatino Linotype", "Book Antiqua", Palatino, Georgia, serif;
  font-size: var(--fs-xl);
  font-weight: 400;
  letter-spacing: 0.035em;
  line-height: 1.25;
}

.outcome-list {
  display: grid;
  gap: 6px;
  margin: 10px 0 0;
  padding: 0;
  list-style: none;
}

/* A chip per outcome, matching Prism Crafting's .chip: a bordered fill reads as
   one scannable object where the shared bottom rule this had read as a table
   ruled inside a card. Nothing here takes the category dot — these are
   Transfiguration outcomes, not the runtime affix categories that palette
   encodes, and a dot with no legend behind it is decoration. */
.outcome-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 4px 8px;
  border: 1px solid var(--edge-soft);
  border-radius: var(--radius-sm);
  background: var(--chip-bg);
}

.outcome-copy {
  display: grid;
  min-width: 0;
  gap: 2px;
}

.outcome-name {
  color: var(--ink);
  font-size: var(--fs-base);
  line-height: 1.3;
}

.outcome-class-scope {
  color: #8ec0e7;
  font-size: var(--fs-2xs);
  line-height: 1.35;
}

.outcome-name-line {
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
}

/* The "i" is drawn here rather than typed into the markup, so it renders the
   same whatever the reader's fonts cover. Sized against .outcome-name's 14px
   rather than the page's, so the badge tracks the row it annotates; the
   .outcome-family-info override below does the same against the 20px heading
   it sits in. */
.outcome-info,
.outcome-family-info {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 15px;
  height: 15px;
  border: 1px solid rgb(196 154 84 / 46%);
  border-radius: 50%;
  color: #d2ad70;
  cursor: help;
  font-size: var(--fs-3xs);
  font-weight: 700;
  line-height: 1;
}

.outcome-info::before,
.outcome-family-info::before {
  content: "i";
}

.outcome-info:hover,
.outcome-family-info:hover {
  border-color: var(--accent);
  color: var(--accent-ink);
}

.outcome-info:focus-visible,
.outcome-family-info:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Set against the heading's serif 20px, and nudged off its baseline: the badge
   is a circle, so sitting it on the text baseline drops it visibly low beside
   a cap-height word. */
.outcome-family-info {
  width: 17px;
  height: 17px;
  margin-left: 9px;
  font-size: var(--fs-2xs);
  vertical-align: 2px;
}

.outcome-meta {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 7px;
}

/* Prism Crafting's .chip .chance: same colour, weight and tabular figures, so a
   percentage reads the same on both pages. */
.outcome-chance {
  min-width: 54px;
  color: var(--accent-ink);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  text-align: right;
}

/* A candidate range, not a resolved chance — but it reads in the same column as
   a stat row's percentage and is the largest figure on the page, so it carries
   the same emphasis colour. A dimmer one made the biggest number the hardest to
   see. The "candidate rate" caption beneath it, and its own class, are what keep
   it from being read as resolved: a stat row's caption below names a share, so
   this one has to say what kind of number it is rather than only how big. */
.outcome-range {
  min-width: 54px;
  color: var(--accent-ink);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  text-align: right;
  white-space: nowrap;
}

.outcome-chance-stack {
  display: grid;
  justify-items: end;
  gap: 1px;
}

.outcome-chance-detail {
  color: var(--faint);
  font-size: var(--fs-3xs);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.outcome-chance-placeholder {
  min-width: 54px;
  color: var(--faint);
  font-size: var(--fs-xs);
  text-align: right;
}

/* The legend badge keeps the circle: it introduces the letter inside a line of
   running text, so it has to be findable there. The card marker below does not —
   it is already in a column of its own. */
.outcome-key-badge {
  display: inline-grid;
  place-items: center;
  width: 18px;
  height: 18px;
  border: 1px solid rgb(196 154 84 / 46%);
  border-radius: 50%;
  color: #d2ad70;
  font-size: var(--fs-3xs);
  font-weight: 700;
  line-height: 1;
  vertical-align: -4px;
}

/* A bare letter in a fixed gutter *before* the figure, not a circle after it.
   Ordered first so every percentage ends on the card's right edge whether or
   not its row is Entropic: as a trailing badge it pushed the marked rows'
   numbers left and broke the one column a reader scans. The fixed width keeps
   the numbers themselves aligned across marked and unmarked rows. */
.outcome-entropic {
  display: inline-block;
  order: -1;
  flex: 0 0 auto;
  width: 9px;
  color: rgb(210 173 112 / 72%);
  font-size: var(--fs-3xs);
  font-weight: 700;
  line-height: 1.3;
  text-align: center;
}

/* The key states the Entropic recipe's whole split, which is a paragraph rather
   than a phrase — so the summary carries the marker and the sentence sits in the
   same boxed body every other note here uses. */
.outcome-key-text {
  margin: 6px 0 0;
  padding: 9px 11px;
  border: 1px solid var(--edge-soft);
  border-radius: var(--radius-sm);
  background: var(--panel);
  color: var(--ink);
  font-size: var(--fs-sm);
  line-height: 1.45;
}

.checklist-section {
  scroll-margin-top: 110px;

  /* The column titles sit above the panel rather than inside it, so they have to
     clear what the rows below sit behind: the panel's horizontal padding plus
     its 1px border. Kept as one variable because both the titles' own paddings
     are derived from it, and the 480px breakpoint retunes the panel's padding. */
  --panel-gutter: 21px;
}

.checklist-panel {
  border: 1px solid var(--edge-soft);
  border-radius: var(--radius-lg);
  background: rgb(22 30 42 / 90%);
  box-shadow: 0 18px 50px rgb(0 0 0 / 18%);
}

.toolbar {
  position: sticky;
  top: 0;
  z-index: 10;
  margin-bottom: 12px;
  padding: 12px 0 10px;
  border-bottom: 1px solid var(--edge);
  background: var(--ground);
  display: flex;
  flex-direction: column;
  gap: 9px;
}

/* .btnrow and .rowlabel are shared/chrome.css, including the narrow-viewport
   rule that gives the label its own line. */

/* No vertical padding: every row centres its content in a 48px box against the
   rule above it, so a top or bottom gutter would leave the first and last rows
   sitting 16px off the rhythm the others keep. The card's own border is their
   separator. (The column titles used to fill that top gutter; they are outside
   the panel now.) */
.checklist-panel {
  width: 100%;
  padding: 0 20px;
}

.checklist {
  display: grid;
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: checklist-step;
}

/* Titles for the two columns of the panel below, so the track sizes and gap here
   mirror the last two tracks of .task and every padding carries --panel-gutter. */
.checklist-columns {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(220px, 0.85fr);
  gap: 16px;
  padding: 0 calc(8px + var(--panel-gutter)) 8px calc(94px + var(--panel-gutter));
  color: var(--faint);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.task {
  display: grid;
  grid-template-columns: 28px 30px minmax(0, 1fr) minmax(220px, 0.85fr);
  align-items: center;
  gap: 12px;
  min-height: 48px;
  padding: 8px;
  border-top: 1px solid var(--edge-soft);
  counter-increment: checklist-step;
}

/* The rule is a separator between rows. With the column titles moved outside the
   panel there is nothing above the first row to separate it from, and the line
   read as a stray rule across the top of the card. */
.task:first-child {
  border-top: 0;
}

.task::before {
  content: counter(checklist-step);
  display: grid;
  flex: 0 0 28px;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 1px solid var(--edge);
  border-radius: 50%;
  color: var(--faint);
  font-size: var(--fs-base);
  font-weight: 700;
}

.task-icon {
  display: grid;
  flex: 0 0 30px;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 1px solid var(--edge);
  border-radius: var(--radius-md);
  background: rgb(9 13 19 / 50%);
  color: var(--muted);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-3xl);
  line-height: 1;
}

.task[data-task="affixes"] .task-icon {
  color: var(--accent-ink);
}

.task[data-task="power-legendary"] .task-icon {
  border-color: #8d5a31;
  color: #df8d47;
}

.task[data-task="power-unique"] .task-icon {
  border-color: #7c6547;
  color: #cdae86;
}

.task[data-task="masterwork"] .task-icon {
  border-color: #8c7541;
  color: #f1ce6b;
  text-shadow: 0 0 10px rgb(241 206 107 / 35%);
}

.task[data-task="temper"] .task-icon {
  border-color: #805c42;
  color: #d49a6e;
}

/* One step satisfied one of two ways: the rows share a step number, are bracketed
   together by a left rail, and the second is marked "or" instead of being counted. */
.task-choice {
  box-shadow: inset 3px 0 0 rgb(137 180 192 / 34%);
}

.task-choice-alt {
  border-top: 1px dashed var(--edge-soft);
  counter-increment: none;
}

.task-choice-alt::before {
  content: "or";
  border-style: dashed;
  font-size: var(--fs-xs);
  font-weight: 400;
  font-style: italic;
}

.task-choice .task-flag {
  padding: 1px 6px;
  background: transparent;
  border: 1px solid var(--edge);
  color: var(--faint);
}

.task-choice[data-task="power-legendary"] .task-flag {
  border-color: #8d5a31;
  color: #df8d47;
}

.task-choice[data-task="power-unique"] .task-flag {
  border-color: #7c6547;
  color: #cdae86;
}

/* #dc9bf2 is the game's own Mythic rarity colour — see --mythic in
   apps/whirlwind-dps/app.css and rarityColors.mythic in apps/loot-filter/data.json. */
.task-choice[data-task="power-mythic-unique"] .task-flag {
  border-color: #8a5a9e;
  color: #dc9bf2;
}

/* Symmetric margin, because with the panel's vertical padding gone this callout
   is what sits against the card's bottom edge on an amulet. */
.task-exceptional {
  margin: 8px 0;
  padding: 10px 8px;
  border: 1px solid #82643a;
  border-radius: var(--radius-md);
  background: rgb(85 58 23 / 20%);
}

.task-exceptional::before {
  content: "!";
  border-color: #b89053;
  color: #e0b36d;
}

.task-exceptional .task-icon {
  border-color: #b89053;
  color: #e0b36d;
}

.task-copy {
  display: grid;
  align-content: center;
  gap: 3px;
  min-width: 0;
}

/* A squeezed column (the two-column desktop layout, or a narrow phone) must
   wrap a long single word like "Masterworking" onto another line rather than
   let it overflow its grid track and print on top of the next column. */
.task-flag,
.task-title,
.task-status {
  overflow-wrap: break-word;
  word-break: break-word;
}

.task-sockets {
  border: 0;
  border-top: 1px solid var(--edge-soft);
  border-radius: 0;
  background: transparent;
}

.task-sockets::before {
  border-color: var(--edge);
  color: var(--faint);
}

.task-sockets .task-icon {
  border-color: var(--accent);
  color: var(--accent-ink);
  box-shadow: none;
}

.task-flag {
  width: fit-content;
  padding: 2px 7px;
  border-radius: var(--radius-sm);
  background: #9c7134;
  color: #fff4dd;
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.task-title {
  color: var(--ink);
  font-size: var(--fs-lg);
  font-weight: 650;
}

.task-status {
  display: flex;
  align-items: center;
  gap: 7px;
  color: var(--muted);
  font-size: var(--fs-lg);
  font-weight: 600;
}

.socket-icons {
  display: inline-flex;
  gap: 5px;
}

.socket-icon {
  display: inline-grid;
  width: 22px;
  height: 22px;
  place-items: center;
  border: 1px solid rgb(137 180 192 / 55%);
  border-radius: 50%;
  background: rgb(137 180 192 / 10%);
  color: var(--accent-ink);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-md);
  line-height: 1;
  box-shadow: inset 0 0 8px rgb(137 180 192 / 12%);
}

.status-icon {
  display: inline-grid;
  flex: 0 0 22px;
  width: 22px;
  height: 22px;
  place-items: center;
  border: 1px solid rgb(199 164 86 / 45%);
  border-radius: var(--radius-lg);
  background: rgb(199 164 86 / 12%);
  color: #e0b36d;
  font-size: var(--fs-base);
  line-height: 1;
}

.task-exceptional .task-status {
  color: #e0b36d;
}

/* .noscript joined .bootfail in apps/shared/base.css — same notice, same box. */

@media (max-width: 760px) {
  .checklist-columns {
    grid-template-columns: minmax(0, 1fr) minmax(150px, 1fr);
    padding-left: calc(94px + var(--panel-gutter));
  }

  .task {
    grid-template-columns: 28px 30px minmax(0, 1fr) minmax(150px, 1fr);
    gap: 10px;
  }
}

@media (max-width: 480px) {
  .checklist-panel {
    padding: 0 14px;
  }

  /* Follows the panel padding directly above, so the titles keep clearing it. */
  .checklist-section {
    --panel-gutter: 15px;
  }

  .checklist-columns {
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
    gap: 10px;
    padding-left: calc(82px + var(--panel-gutter));
  }

  .task {
    grid-template-columns: 26px 28px minmax(0, 0.85fr) minmax(0, 1.15fr);
    gap: 8px;
  }

  .task::before {
    width: 26px;
    height: 26px;
  }

  .task-icon {
    width: 28px;
    height: 28px;
    font-size: var(--fs-2xl);
  }

  .task-title,
  .task-status {
    font-size: var(--fs-md);
  }

  .atlas-panel {
    padding: 14px;
  }
}
