feat(scripts/diff-flat): show feature descriptions in aligned column
diff --git a/scripts/diff-flat.js b/scripts/diff-flat.js
index 87a95f6..a211403 100644
--- a/scripts/diff-flat.js
+++ b/scripts/diff-flat.js
@@ -685,9 +685,83 @@
   const commonName =
     options.format === 'html' ? `<h3>${prefix}</h3>` : `${prefix}`;
 
+  /**
+   * Renders a colored inline diff between two stringified field values,
+   * matching the convention used elsewhere: green for additions in head, red
+   * for removals from base. Returns an empty string when the diff would be
+   * empty (e.g. null → "mirror" / "false").
+   * @param {string} baseValue stringified base value (or `"null"`).
+   * @param {string} headValue stringified head value (or `"null"`).
+   * @returns {string} the colored diff string.
+   */
+  const formatValueDiff = (baseValue, headValue) => {
+    const splitRegexp =
+      /(?<=^")|(?<=[\],/ ])|(?=[[,/ ])|(?="$)|(?<=\d)(?=−)|(?<=−)(?=\d)|(?=#)/;
+    let headValueForDiff = headValue;
+    let baseValueForDiff = baseValue;
+
+    if (baseValue == 'null') {
+      baseValueForDiff = '';
+      if (headValue == '"mirror"' || headValue == '"false"') {
+        headValueForDiff = '';
+      }
+    } else if (headValue == 'null') {
+      headValueForDiff = '';
+    }
+
+    return diffArrays(
+      headValueForDiff.split(splitRegexp),
+      baseValueForDiff.split(splitRegexp),
+    )
+      .map((part) => {
+        // Note: removed/added is deliberately inverted here, to have
+        // additions first.
+        const value = part.value.join('');
+        if (part.removed) {
+          return options.format == 'html'
+            ? `<ins style="color: green">${value}</ins>`
+            : styleText('green', value);
+        } else if (part.added) {
+          return options.format == 'html'
+            ? `<del style="color: red">${value}</del>`
+            : styleText('red', value);
+        }
+        return value;
+      })
+      .join('');
+  };
+
+  /** @type {Set<string>} */
+  const consumedKeys = new Set();
+  for (const [, to] of moves) {
+    consumedKeys.add(`${to}.__compat.description`);
+  }
+  for (const path of [...addedFeatures, ...removedFeatures]) {
+    consumedKeys.add(`${path}.__compat.description`);
+  }
+
+  /**
+   * Returns the colored description diff at a feature path, or empty if
+   * unchanged.
+   * @param {string} path the feature path.
+   * @returns {string} the colored description diff (or empty).
+   */
+  const featureDescriptionDiff = (path) => {
+    const key = `${path}.__compat.description`;
+    const baseValue = JSON.stringify(baseData[key] ?? null);
+    const headValue = JSON.stringify(headData[key] ?? null);
+    if (baseValue === headValue) {
+      return '';
+    }
+    return formatValueDiff(baseValue, headValue);
+  };
+
   let lastKey = '';
 
   for (const key of keys) {
+    if (consumedKeys.has(key)) {
+      continue;
+    }
     const baseValue = JSON.stringify(baseData[key] ?? null);
     const headValue = JSON.stringify(headData[key] ?? null);
     if (baseValue === headValue) {
@@ -702,42 +776,7 @@
       options,
     );
 
-    const splitRegexp =
-      /(?<=^")|(?<=[\],/ ])|(?=[[,/ ])|(?="$)|(?<=\d)(?=−)|(?<=−)(?=\d)|(?=#)/;
-    let headValueForDiff = headValue;
-    let baseValueForDiff = baseValue;
-
-    if (baseValue == 'null') {
-      baseValueForDiff = '';
-      if (headValue == '"mirror"' || headValue == '"false"') {
-        // Ignore initial "mirror"/"false" values.
-        headValueForDiff = '';
-      }
-    } else if (headValue == 'null') {
-      headValueForDiff = '';
-    }
-
-    const valueDiff = diffArrays(
-      headValueForDiff.split(splitRegexp),
-      baseValueForDiff.split(splitRegexp),
-    )
-      .map((part) => {
-        // Note: removed/added is deliberately inversed here, to have additions first.
-        const value = part.value.join('');
-        if (part.removed) {
-          return options.format == 'html'
-            ? `<ins style="color: green">${value}</ins>`
-            : styleText('green', value);
-        } else if (part.added) {
-          return options.format == 'html'
-            ? `<del style="color: red">${value}</del>`
-            : styleText('red', value);
-        }
-
-        return value;
-      })
-      .join('');
-
+    const valueDiff = formatValueDiff(baseValue, headValue);
     const value = valueDiff;
 
     if (!value.length) {
@@ -778,7 +817,12 @@
     lastKey = key;
   }
 
-  if (groups.size === 0) {
+  if (
+    groups.size === 0 &&
+    !addedFeatures.length &&
+    !removedFeatures.length &&
+    !moves.size
+  ) {
     console.log('✔ No changes.');
     return;
   }
@@ -863,69 +907,99 @@
     }
   };
 
-  if (addedFeatures.length) {
-    if (options.format === 'html') {
-      console.log('<h4>New features</h4>');
-      console.log('<ul>');
-      for (const path of addedFeatures) {
-        const lastDot = path.lastIndexOf('.');
-        const parent = lastDot === -1 ? '' : path.slice(0, lastDot + 1);
-        const leaf = lastDot === -1 ? path : path.slice(lastDot + 1);
-        console.log(
-          `<li>${parent}<ins style="color: green">${leaf}</ins></li>`,
-        );
-      }
-      console.log('</ul>');
-    } else {
-      console.log(styleText('bold', 'New features:'));
-      for (const path of addedFeatures) {
-        const lastDot = path.lastIndexOf('.');
-        const parent = lastDot === -1 ? '' : path.slice(0, lastDot + 1);
-        const leaf = lastDot === -1 ? path : path.slice(lastDot + 1);
-        console.log(`  ${parent}${styleText('green', leaf)}`);
-      }
-      console.log('');
-    }
+  /**
+   * @typedef {object} ListingItem
+   * @property {string} section section header.
+   * @property {string} rendered styled key (path or move).
+   * @property {number} visibleLen visible length of `rendered` (no styling).
+   * @property {string} desc styled description diff (or empty).
+   */
+
+  /** @type {ListingItem[]} */
+  const listingItems = [];
+  for (const path of addedFeatures) {
+    const lastDot = path.lastIndexOf('.');
+    const parent = lastDot === -1 ? '' : path.slice(0, lastDot + 1);
+    const leaf = lastDot === -1 ? path : path.slice(lastDot + 1);
+    const styledLeaf =
+      options.format === 'html'
+        ? `<ins style="color: green">${leaf}</ins>`
+        : styleText('green', leaf);
+    listingItems.push({
+      section: 'New features',
+      rendered: `${parent}${styledLeaf}`,
+      visibleLen: path.length,
+      desc: featureDescriptionDiff(path),
+    });
+  }
+  for (const path of removedFeatures) {
+    const lastDot = path.lastIndexOf('.');
+    const parent = lastDot === -1 ? '' : path.slice(0, lastDot + 1);
+    const leaf = lastDot === -1 ? path : path.slice(lastDot + 1);
+    const styledLeaf =
+      options.format === 'html'
+        ? `<del style="color: red">${leaf}</del>`
+        : styleText('red', leaf);
+    listingItems.push({
+      section: 'Removed features',
+      rendered: `${parent}${styledLeaf}`,
+      visibleLen: path.length,
+      desc: featureDescriptionDiff(path),
+    });
+  }
+  for (const [from, to] of moves) {
+    const rendered = formatMove(from, to, options);
+    const visibleLen =
+      options.format === 'html'
+        ? rendered.replace(/<[^>]+>/g, '').length
+        : stripAnsi(rendered).length;
+    listingItems.push({
+      section: 'Moved features',
+      rendered,
+      visibleLen,
+      desc: featureDescriptionDiff(to),
+    });
   }
 
-  if (removedFeatures.length) {
-    if (options.format === 'html') {
-      console.log('<h4>Removed features</h4>');
-      console.log('<ul>');
-      for (const path of removedFeatures) {
-        const lastDot = path.lastIndexOf('.');
-        const parent = lastDot === -1 ? '' : path.slice(0, lastDot + 1);
-        const leaf = lastDot === -1 ? path : path.slice(lastDot + 1);
-        console.log(`<li>${parent}<del style="color: red">${leaf}</del></li>`);
+  if (listingItems.length) {
+    const maxLen = Math.max(...listingItems.map((i) => i.visibleLen));
+    const hasAnyDesc = listingItems.some((i) => i.desc);
+    let lastSection = '';
+    for (const item of listingItems) {
+      if (item.section !== lastSection) {
+        if (lastSection) {
+          console.log('');
+        }
+        const title = `${item.section}:`;
+        const styledTitle =
+          options.format === 'html'
+            ? `<strong>${title}</strong>`
+            : styleText('bold', title);
+        let header = styledTitle;
+        if (hasAnyDesc) {
+          const padding = ' '.repeat(Math.max(1, maxLen + 3 - title.length));
+          const descLabel = 'description =';
+          header +=
+            padding +
+            (options.format === 'html'
+              ? `<em>${descLabel}</em>`
+              : styleText('italic', descLabel));
+        }
+        console.log(header);
+        lastSection = item.section;
       }
-      console.log('</ul>');
-    } else {
-      console.log(styleText('bold', 'Removed features:'));
-      for (const path of removedFeatures) {
-        const lastDot = path.lastIndexOf('.');
-        const parent = lastDot === -1 ? '' : path.slice(0, lastDot + 1);
-        const leaf = lastDot === -1 ? path : path.slice(lastDot + 1);
-        console.log(`  ${parent}${styleText('red', leaf)}`);
+      let line = `  ${item.rendered}`;
+      if (item.desc) {
+        const padding = ' '.repeat(1 + maxLen - item.visibleLen);
+        const styledDesc =
+          options.format === 'html'
+            ? `<em>${item.desc}</em>`
+            : styleText('italic', item.desc);
+        line += padding + styledDesc;
       }
-      console.log('');
+      console.log(line);
     }
-  }
-
-  if (moves.size) {
-    if (options.format === 'html') {
-      console.log('<h4>Moved features</h4>');
-      console.log('<ul>');
-      for (const [from, to] of moves) {
-        console.log(`<li>${formatMove(from, to, options)}</li>`);
-      }
-      console.log('</ul>');
-    } else {
-      console.log(styleText('bold', 'Moved features:'));
-      for (const [from, to] of moves) {
-        console.log(`  ${formatMove(from, to, options)}`);
-      }
-      console.log('');
-    }
+    console.log('');
   }
 
   if (addedFeatures.length || removedFeatures.length || moves.size) {