Repository: GoogleChrome/lighthouse
Pull request #8857 (2019-05-05 00:19:51)

audit: add apple-touch-icon audit
**Summary**
Adds a new audit to the PWA category to check for apple-touch-icon link for iOS installability.

**Related Issues/PRs**
fixes: #7890 

exterkamp: Reading the docs more:
* You can include a startup image as well `<link rel="apple-touch-startup-image" href="/launch.png">`
* You can specify a launch icon title with the `<title>` or `<meta name="apple-mobile-web-app-title" content="AppTitle">`

Don't know if we want to include more optional-ish things like this in a more generic iOS PWA health audit, or more iOS specific PWA audits like this one.
paulirish: > Don't know if we want to include more optional-ish things like this in a more generic iOS PWA health audit, or more iOS specific PWA audits like this one.

In the future, perhaps. But not in this sprint. :)

Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bcc51735a..7b4d425a0 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -81,7 +81,7 @@ yarn run update:sample-artifacts ScriptElements # update just one artifact
 yarn run update:sample-json # update sample LHR based on sample artifacts
 ```
 
-When updating all artifacts, usually you'll need to revert changes to the `*.devtoolslog.json` and `*.trace.json` files and manually review changes to `artifacts.json` to make sure they are related to your work.
+When updating artifacts, usually you'll need to revert changes to the `*.devtoolslog.json` and `*.trace.json` files and manually review changes to `artifacts.json` to make sure they are related to your work.
 
 ## Tracking Errors
 
diff --git a/docs/throttling.md b/docs/throttling.md
index 761c660ae..c54b162cd 100644
--- a/docs/throttling.md
+++ b/docs/throttling.md
@@ -7,22 +7,22 @@ This is the standard recommendation for mobile throttling:
 - Throughput: 1.6Mbps down / 750 Kbps up.
 - Packet loss: none.
 
-These exact figures are used as [Lighthouse's throttling default](https://github.com/GoogleChrome/lighthouse/blob/8f500e00243e07ef0a80b39334bedcc8ddc8d3d0/lighthouse-core/config/constants.js#L19-L26) and represent roughly the bottom 25% of 4G connections and top 25% of 3G connections. They are identical to the [WebPageTest "Mobile 3G - Fast" preset](https://github.com/WPO-Foundation/webpagetest/blob/master/www/settings/connectivity.ini.sample) and, due to a lower latency, slightly faster for some pages than the [WebPageTest "4G" preset](https://github.com/WPO-Foundation/webpagetest/blob/master/www/settings/connectivity.ini.sample).
+These exact figures are used as [Lighthouse's throttling default](https://github.com/GoogleChrome/lighthouse/blob/8f500e00243e07ef0a80b39334bedcc8ddc8d3d0/lighthouse-core/config/constants.js#L19-L26) and represent roughly the bottom 25% of 4G connections and top 25% of 3G connections (In Lighthouse it is sometimes called "Slow 4G" used to be labeled as "Fast 3G"). This preset is identical to the [WebPageTest's "Mobile 3G - Fast"](https://github.com/WPO-Foundation/webpagetest/blob/master/www/settings/connectivity.ini.sample) and, due to a lower latency, slightly faster for some pages than the [WebPageTest "4G" preset](https://github.com/WPO-Foundation/webpagetest/blob/master/www/settings/connectivity.ini.sample).
 
-## Throttling basics
+## Types of throttling
 
-1. Simulated throttling, which Lighthouse uses by default, is computed throttling _after a trace has been recorded_ which makes it very fast and deterministic. However, due to the imperfect nature of predicting alternate execution paths, there is inherent inaccuracy that is summarized in this doc: [Lighthouse Metric Variability and Accuracy](https://docs.google.com/document/d/1BqtL-nG53rxWOI5RO0pItSRPowZVnYJ_gBEQCJ5EeUE/edit). The TLDR: while it's roughly as accurate or better than DevTools throttling for most sites, it suffers from edge cases and a deep investigation to performance should use _Packet-level_ throttling tools.
-1. The DevTools network throttling, which Lighthouse uses for `throttlingMethod='devtools'`, is implemented within Chrome at the _request-level_. As a result, it has some downsides that are now summarized in this doc: [Network Throttling & Chrome - status](https://docs.google.com/document/d/1TwWLaLAfnBfbk5_ZzpGXegPapCIfyzT4MWuZgspKUAQ/edit). The TLDR: while it's a [decent approximation](https://docs.google.com/document/d/10lfVdS1iDWCRKQXPfbxEn4Or99D64mvNlugP1AQuFlE/edit), it's not a sufficient model of a slow connection. The [multipliers used in Lighthouse](https://github.com/GoogleChrome/lighthouse/blob/3be483287a530fb560c843b7299ef9cfe91ce1cc/lighthouse-core/lib/emulation.js#L33-L39) attempt to correct for the differences.
-1. _Proxy-level_ throttling tools do not affect UDP data, so they're not recommended.
-1. _Packet-level_ throttling tools are able to make the most accurate network simulation.
+Within web performance testing, there are four typical styles of throttling:
 
-## How do I get high-quality packet-level throttling?
+1. **_Simulated throttling_**, which Lighthouse uses by **default**, uses a simulation of a page load, based on the data observed in the initial unthrottled load. This approach which makes it both very fast and deterministic. However, due to the imperfect nature of predicting alternate execution paths, there is inherent inaccuracy that is summarized in this doc: [Lighthouse Metric Variability and Accuracy](https://docs.google.com/document/d/1BqtL-nG53rxWOI5RO0pItSRPowZVnYJ_gBEQCJ5EeUE/edit). The TLDR: while it's roughly as accurate or better than DevTools throttling for most sites, it suffers from edge cases and a deep investigation to performance should use _Packet-level_ throttling tools.
+1. **_Request-level_** throttling, also referred to as _DevTools throttling_ or in the Audits Panel as _Applied throttling_, is how throttling is implemented with Chrome DevTools. In real mobile connectivity, latency affects things at the packet level rather than the request level. As a result, this throttling isn't highly accurate. It also has a few more downsides that are summarized in [Network Throttling & Chrome - status](https://docs.google.com/document/d/1TwWLaLAfnBfbk5_ZzpGXegPapCIfyzT4MWuZgspKUAQ/edit). The TLDR: while it's a [decent approximation](https://docs.google.com/document/d/10lfVdS1iDWCRKQXPfbxEn4Or99D64mvNlugP1AQuFlE/edit), it's not a sufficient model of a slow connection. The [multipliers used in Lighthouse](https://github.com/GoogleChrome/lighthouse/blob/3be483287a530fb560c843b7299ef9cfe91ce1cc/lighthouse-core/lib/emulation.js#L33-L39) attempt to correct for the differences.
+1. **_Proxy-level_** throttling tools do not affect UDP data, so they're decent, but not ideal.
+1. **_Packet-level_** throttling tools are able to make the most accurate network simulation. While this approach can model real network conditions most effectively, it also can introduce [more variance](https://docs.google.com/document/d/1BqtL-nG53rxWOI5RO0pItSRPowZVnYJ_gBEQCJ5EeUE/edit) than request-level or simulated throttling. [WebPageTest uses](https://github.com/WPO-Foundation/wptagent/blob/master/docs/remote_trafficshaping.md) packet-level throttling.
 
-If you want to use more accurate throttling, read on.
+Lighthouse, by default, uses simulated throttling as it provides both quick evaluation and minimized variance. However, some may want to experiment with more accurate throttling...
 
-This Performance Calendar article, [Testing with Realistic Networking Conditions](https://calendar.perfplanet.com/2016/testing-with-realistic-networking-conditions/), has a good explanation of packet-level traffic shaping (which applies across TCP/UDP/ICMP) and recommendations.
+## How do I get packet-level throttling?
 
-### Using `comcast` for network throttling
+This Performance Calendar article, [Testing with Realistic Networking Conditions](https://calendar.perfplanet.com/2016/testing-with-realistic-networking-conditions/), has a good explanation of packet-level traffic shaping (which applies across TCP/UDP/ICMP) and recommendations.
 
 The `comcast` Go package appears to be the most usable Mac/Linux commandline app for managing your network connection. Important to note: it changes your **entire** machine's network interface. Also, **`comcast` requires `sudo`** (as all packet-level shapers do).
 
diff --git a/lighthouse-core/audits/apple-touch-icon.js b/lighthouse-core/audits/apple-touch-icon.js
new file mode 100644
index 000000000..46313a29e
--- /dev/null
+++ b/lighthouse-core/audits/apple-touch-icon.js
@@ -0,0 +1,71 @@
+/**
+ * @license Copyright 2019 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
+ */
+'use strict';
+
+const Audit = require('./audit.js');
+const i18n = require('../lib/i18n/i18n.js');
+
+/**
+ * @fileoverview Audits if a page has an `apple-touch-icon` link element with a valid href.
+ */
+
+const UIStrings = {
+  /** Title of a Lighthouse audit that tells the user that their site contains a vaild touch icon. This descriptive title is shown when the page contains a valid iOS touch icon. "apple-touch-icon" is an HTML attribute value and should not be translated. */
+  title: 'Provides a valid `apple-touch-icon`',
+  /** Title of a Lighthouse audit that tells the user that their site contains a vaild touch icon. This descriptive title is shown when the page does not contain a valid iOS touch icon. "apple-touch-icon" is an HTML attribute value and should not be translated. */
+  failureTitle: 'Does not provide a valid `apple-touch-icon`',
+  /** Description of a Lighthouse audit that tells the user what having a valid apple-touch-icon does. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. "apple-touch-icon" is an HTML attribute value and should not be translated. */
+  description: 'For ideal appearance on iOS when users add to the home screen, define an ' +
+  'apple-touch-icon. It must point to a non-transparent 192px (or 180px) square PNG. ' +
+  '[Learn More](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/).',
+  /** Warning that HTML attribute `apple-touch-icon-precomposed` should not be used in favor of `apple-touch-icon`.  "apple-touch-icon-precomposed" and "apple-touch-icon" are HTML attribute values and should not be translated. */
+  precomposedWarning: '`apple-touch-icon-precomposed` is out of date; ' +
+  '`apple-touch-icon` is preferred.',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
+
+class AppleTouchIcon extends Audit {
+  /**
+   * @return {LH.Audit.Meta}
+   */
+  static get meta() {
+    return {
+      id: 'apple-touch-icon',
+      title: str_(UIStrings.title),
+      failureTitle: str_(UIStrings.failureTitle),
+      description: str_(UIStrings.description),
+      requiredArtifacts: ['LinkElements'],
+    };
+  }
+
+  /**
+   * @param {LH.Artifacts} artifacts
+   * @return {LH.Audit.Product}
+   */
+  static audit(artifacts) {
+    const appleTouchIcons = artifacts.LinkElements
+      .filter(el => el.rel === 'apple-touch-icon' || el.rel === 'apple-touch-icon-precomposed')
+      .filter(el => !!el.href);
+
+    // Audit passes if an `apple-touch-icon` exists.
+    const passed = appleTouchIcons.length !== 0;
+
+    const warnings = [];
+    if (appleTouchIcons.filter(el => el.rel === 'apple-touch-icon-precomposed').length !== 0
+      && appleTouchIcons.filter(el => el.rel === 'apple-touch-icon').length === 0) {
+      warnings.push(str_(UIStrings.precomposedWarning));
+    }
+
+    return {
+      score: passed ? 1 : 0,
+      warnings,
+    };
+  }
+}
+
+module.exports = AppleTouchIcon;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/performance-budget.js b/lighthouse-core/audits/performance-budget.js
index f47f4f1de..71f3630e9 100644
--- a/lighthouse-core/audits/performance-budget.js
+++ b/lighthouse-core/audits/performance-budget.js
@@ -12,9 +12,9 @@ const i18n = require('../lib/i18n/i18n.js');
 const UIStrings = {
   /** Title of a Lighthouse audit that compares the size and quantity of page resources against targets set by the user. These targets are thought of as "performance budgets" because these metrics impact page performance (i.e. how quickly a page loads). */
   title: 'Performance budget',
-  /** Description of a Lighthouse audit where a user sets budgets for the quantity and size of page resources. No character length limits. */
+  /** Description of a Lighthouse audit where a user sets budgets for the quantity and size of page resources. No character length limits. 'Learn More' becomes link text to additional documentation. */
   description: 'Keep the quantity and size of network requests under the targets ' +
-    'set by the provided performance budget.',
+    'set by the provided performance budget. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).',
   /** [ICU Syntax] Entry in a data table identifying the number of network requests of a particular type. Count will be a whole number. String should be as short as possible to be able to fit well into the table. */
   requestCountOverBudget: `{count, plural,
     =1 {1 request}
diff --git a/lighthouse-core/audits/resource-summary.js b/lighthouse-core/audits/resource-summary.js
index b39add3ae..d7835a9cb 100644
--- a/lighthouse-core/audits/resource-summary.js
+++ b/lighthouse-core/audits/resource-summary.js
@@ -12,9 +12,9 @@ const i18n = require('../lib/i18n/i18n.js');
 const UIStrings = {
   /** Imperative title of a Lighthouse audit that tells the user to minimize the size and quantity of resources used to load the page. */
   title: 'Keep request counts low and transfer sizes small',
-  /** Description of a Lighthouse audit that tells the user that they can setup a budgets for the quantity and size of page resources. No character length limits. */
+  /** Description of a Lighthouse audit that tells the user that they can setup a budgets for the quantity and size of page resources. No character length limits. 'Learn More' becomes link text to additional documentation. */
   description: 'To set budgets for the quantity and size of page resources,' +
-    ' add a budget.json file.',
+    ' add a budget.json file. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).',
   /** [ICU Syntax] Label for an audit identifying the number of requests and kilobytes used to load the page. */
   displayValue: `{requestCount, plural, =1 {1 request} other {# requests}}` +
     ` • { byteCount, number, bytes } KB`,
diff --git a/lighthouse-core/config/default-config.js b/lighthouse-core/config/default-config.js
index 9184e5492..42672983c 100644
--- a/lighthouse-core/config/default-config.js
+++ b/lighthouse-core/config/default-config.js
@@ -179,6 +179,7 @@ const defaultConfig = {
     'critical-request-chains',
     'redirects',
     'installable-manifest',
+    'apple-touch-icon',
     'splash-screen',
     'themed-omnibox',
     'content-width',
@@ -523,6 +524,7 @@ const defaultConfig = {
         {id: 'content-width', weight: 1, group: 'pwa-optimized'},
         {id: 'viewport', weight: 2, group: 'pwa-optimized'},
         {id: 'without-javascript', weight: 1, group: 'pwa-optimized'},
+        {id: 'apple-touch-icon', weight: 1, group: 'pwa-optimized'},
         // Manual audits
         {id: 'pwa-cross-browser', weight: 0},
         {id: 'pwa-page-transitions', weight: 0},
diff --git a/lighthouse-core/lib/i18n/en-US.json b/lighthouse-core/lib/i18n/en-US.json
index e40e4f048..adfc34c8f 100644
--- a/lighthouse-core/lib/i18n/en-US.json
+++ b/lighthouse-core/lib/i18n/en-US.json
@@ -423,6 +423,22 @@
     "message": "`<video>` elements contain a `<track>` element with `[kind=\"description\"]`",
     "description": "Title of an accesibility audit that evaluates if all video elements have child track elements that contain a description of the video content. This title is descriptive of the successful state and is shown to users when no user action is required."
   },
+  "lighthouse-core/audits/apple-touch-icon.js | description": {
+    "message": "For ideal appearance on iOS when users add to the home screen, define an apple-touch-icon. It must point to a non-transparent 192px (or 180px) square PNG. [Learn More](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/).",
+    "description": "Description of a Lighthouse audit that tells the user what having a valid apple-touch-icon does. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. \"apple-touch-icon\" is an HTML attribute value and should not be translated."
+  },
+  "lighthouse-core/audits/apple-touch-icon.js | failureTitle": {
+    "message": "Does not provide a valid `apple-touch-icon`",
+    "description": "Title of a Lighthouse audit that tells the user that their site contains a vaild touch icon. This descriptive title is shown when the page does not contain a valid iOS touch icon. \"apple-touch-icon\" is an HTML attribute value and should not be translated."
+  },
+  "lighthouse-core/audits/apple-touch-icon.js | precomposedWarning": {
+    "message": "`apple-touch-icon-precomposed` is out of date; `apple-touch-icon` is preferred.",
+    "description": "Warning that HTML attribute `apple-touch-icon-precomposed` should not be used in favor of `apple-touch-icon`.  \"apple-touch-icon-precomposed\" and \"apple-touch-icon\" are HTML attribute values and should not be translated."
+  },
+  "lighthouse-core/audits/apple-touch-icon.js | title": {
+    "message": "Provides a valid `apple-touch-icon`",
+    "description": "Title of a Lighthouse audit that tells the user that their site contains a vaild touch icon. This descriptive title is shown when the page contains a valid iOS touch icon. \"apple-touch-icon\" is an HTML attribute value and should not be translated."
+  },
   "lighthouse-core/audits/bootup-time.js | chromeExtensionsWarning": {
     "message": "Chrome extensions negatively affected this page's load performance. Try auditing the page in incognito mode or from a Chrome profile without extensions.",
     "description": "A message displayed in a Lighthouse audit result warning that Chrome extensions on the user's system substantially affected Lighthouse's measurements and instructs the user on how to run again without those extensions."
@@ -744,8 +760,8 @@
     "description": "Descriptive title of a Lighthouse audit that tells the user the server latencies observed from each origin the page connected to. This is displayed in a list of audit titles that Lighthouse generates."
   },
   "lighthouse-core/audits/performance-budget.js | description": {
-    "message": "Keep the quantity and size of network requests under the targets set by the provided performance budget.",
-    "description": "Description of a Lighthouse audit where a user sets budgets for the quantity and size of page resources. No character length limits."
+    "message": "Keep the quantity and size of network requests under the targets set by the provided performance budget. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).",
+    "description": "Description of a Lighthouse audit where a user sets budgets for the quantity and size of page resources. No character length limits. 'Learn More' becomes link text to additional documentation."
   },
   "lighthouse-core/audits/performance-budget.js | requestCountOverBudget": {
     "message": "{count, plural,\n    =1 {1 request}\n    other {# requests}\n   }",
@@ -764,8 +780,8 @@
     "description": "Imperative title of a Lighthouse audit that tells the user to eliminate the redirects taken through multiple URLs to load the page. This is shown in a list of audits that Lighthouse generates."
   },
   "lighthouse-core/audits/resource-summary.js | description": {
-    "message": "To set budgets for the quantity and size of page resources, add a budget.json file.",
-    "description": "Description of a Lighthouse audit that tells the user that they can setup a budgets for the quantity and size of page resources. No character length limits."
+    "message": "To set budgets for the quantity and size of page resources, add a budget.json file. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).",
+    "description": "Description of a Lighthouse audit that tells the user that they can setup a budgets for the quantity and size of page resources. No character length limits. 'Learn More' becomes link text to additional documentation."
   },
   "lighthouse-core/audits/resource-summary.js | displayValue": {
     "message": "{requestCount, plural, =1 {1 request} other {# requests}} • { byteCount, number, bytes } KB",
diff --git a/lighthouse-core/lib/i18n/locales/ta.json b/lighthouse-core/lib/i18n/locales/ta.json
index a081a20f8..ba2e010b9 100644
--- a/lighthouse-core/lib/i18n/locales/ta.json
+++ b/lighthouse-core/lib/i18n/locales/ta.json
@@ -447,10 +447,10 @@
     "message": "மதிப்பு"
   },
   "lighthouse-core/audits/dobetterweb/dom-size.js | description": {
-    "message": "Browser engineers recommend pages contain fewer than ~1,500 DOM elements. The sweet spot is a tree depth < 32 elements and fewer than 60 children/parent element. A large DOM can increase memory usage, cause longer [style calculations](https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations), and produce costly [layout reflows](https://developers.google.com/speed/articles/reflow). [Learn more](https://developers.google.com/web/tools/lighthouse/audits/dom-size)."
+    "message": "~1,500 DOM உறுப்புகளுக்குக் குறைவான உறுப்புகளைக் கொண்ட பக்கங்களை உலாவிப் பொறியாளர்கள் பரிந்துரைக்கின்றனர். 32 உறுப்புகளுக்குக் குறைவான கிளை அடுக்குகளும் 60க்குக் குறைவான துணைக்கிளை/கிளை உறுப்புகளும் இருப்பது உகந்தது. ஒரு பெரிய DOM, நினைவகப் பயன்பாட்டை அதிகரிக்கலாம், [ஸ்டைல் கணக்கீடுகளை] நீட்டிக்கலாம், மேலும் (https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations) அதிகச் செலவு பிடிக்கும் [தளவமைப்பு மறுசீராக்கங்களை] ஏற்படுத்தலாம் (https://developers.google.com/speed/articles/reflow). [மேலும் அறிக](https://developers.google.com/web/tools/lighthouse/audits/dom-size)."
   },
   "lighthouse-core/audits/dobetterweb/dom-size.js | displayValue": {
-    "message": "{itemCount, plural,\n    =1 {1 element}\n    other {# elements}\n    }"
+    "message": "{itemCount,plural, =1{1 உறுப்பு}other{# உறுப்புகள்}}"
   },
   "lighthouse-core/audits/dobetterweb/dom-size.js | failureTitle": {
     "message": "அபரிமிதமான DOM அளவைத் தவிர்க்கவும்"
@@ -459,7 +459,7 @@
     "message": "அதிகபட்ச DOM கிளை அடுக்கு"
   },
   "lighthouse-core/audits/dobetterweb/dom-size.js | statisticDOMElements": {
-    "message": "Total DOM Elements"
+    "message": "மொத்த DOM உறுப்புக்கள்"
   },
   "lighthouse-core/audits/dobetterweb/dom-size.js | statisticDOMWidth": {
     "message": "அதிகபட்சத் துணை உறுப்புகள்"
@@ -876,22 +876,22 @@
     "message": "SEO"
   },
   "lighthouse-core/config/default-config.js | seoContentGroupDescription": {
-    "message": "Format your HTML in a way that enables crawlers to better understand your app’s content."
+    "message": "ஆப்ஸின் உள்ளடக்கத்தை crawlerகள் நன்கு புரிந்துகொள்ள வசதியாக HTMLலைப் பொருத்தமாக வடிவமைக்கவும்."
   },
   "lighthouse-core/config/default-config.js | seoContentGroupTitle": {
-    "message": "Content Best Practices"
+    "message": "உள்ளடக்கம் தொடர்பான சிறந்த நடைமுறைகள்"
   },
   "lighthouse-core/config/default-config.js | seoCrawlingGroupDescription": {
-    "message": "To appear in search results, crawlers need access to your app."
+    "message": "தேடல் முடிவுகளில் தோன்ற உங்கள் ஆப்ஸிற்கான அணுகல் crawlerகளுக்குத் தேவை."
   },
   "lighthouse-core/config/default-config.js | seoCrawlingGroupTitle": {
-    "message": "Crawling and Indexing"
+    "message": "Crawling மற்றும் அட்டவணைப்படுத்துதல்"
   },
   "lighthouse-core/config/default-config.js | seoMobileGroupDescription": {
-    "message": "Make sure your pages are mobile friendly so users don’t have to pinch or zoom in order to read the content pages. [Learn more](https://developers.google.com/search/mobile-sites/)."
+    "message": "பக்கங்களின் உள்ளடக்கத்தைப் பயனர்கள் பின்ச் செய்தோ பெரிதாக்கியோ படிக்க வேண்டிய அவசியம் ஏற்படாதவாறு உங்கள் பக்கங்கள் மொபைலுக்கு ஏற்றவையாக இருக்க வேண்டும். [மேலும் அறிக](https://developers.google.com/search/mobile-sites/)."
   },
   "lighthouse-core/config/default-config.js | seoMobileGroupTitle": {
-    "message": "Mobile Friendly"
+    "message": "மொபைலுக்கேற்றது"
   },
   "lighthouse-core/lib/i18n/i18n.js | columnCacheTTL": {
     "message": "தற்காலிக நினைவக TTL"
@@ -927,7 +927,7 @@
     "message": "உங்கள் பக்கம் ஏற்றப்படுகையில் டிரேஸைப் பதிவுசெய்யும்போது ஏதோ தவறாகிவிட்டது. Lighthouseஸை மீண்டும் இயக்கவும். ({errorCode})"
   },
   "lighthouse-core/lib/lh-error.js | criTimeout": {
-    "message": "Timeout waiting for initial Debugger Protocol connection."
+    "message": "முதல் பிழைதிருத்தும் நெறிமுறை இணைப்பிற்கான நேரமுடிவு காத்திருப்பு."
   },
   "lighthouse-core/lib/lh-error.js | didntCollectScreenshots": {
     "message": "பக்கம் ஏற்றப்பட்டபோது ஸ்க்ரீன்ஷாட்கள் எதையும் Chrome சேகரிக்கவில்லை. பக்கத்தில் உள்ளடக்கம் காண்பிக்கப்படுவதை உறுதிசெய்து, Lighthouseஸை மீண்டும் இயக்கவும். ({errorCode})"
@@ -1020,45 +1020,45 @@
     "message": "எச்சரிக்கைகள்: "
   },
   "stack-packs/packs/wordpress.js | efficient_animated_content": {
-    "message": "Consider uploading your GIF to a service which will make it available to embed as an HTML5 video."
+    "message": "உங்கள் GIFஃபை HTML5 வீடியோவாக உட்பொதிந்து கிடைக்கச் செய்யும் சேவையில் பதிவேற்ற முயலுங்கள்."
   },
   "stack-packs/packs/wordpress.js | offscreen_images": {
-    "message": "Install a [lazy-load WordPress plugin](https://wordpress.org/plugins/search/lazy+load/) that provides the ability to defer any offscreen images, or switch to a theme that provides that functionality. Also consider using [the AMP plugin](https://wordpress.org/plugins/amp/)."
+    "message": "[lazy-load WordPress செருகுநிரலை] நிறுவி (https://wordpress.org/plugins/search/lazy+load/) திரைக்கு வெளியிலுள்ள படங்களைத் தேவையான போது ஏற்றும் திறனைப் பெறலாம் அல்லது அந்தத் திறனுள்ள தீமிற்கு மாறலாம். [AMP செருகுநிரலையும்](https://wordpress.org/plugins/amp/) பயன்படுத்திப் பார்க்கவும்."
   },
   "stack-packs/packs/wordpress.js | render_blocking_resources": {
-    "message": "There are a number of WordPress plugins that can help you [inline critical assets](https://wordpress.org/plugins/search/critical+css/) or [defer less important resources](https://wordpress.org/plugins/search/defer+css+javascript/). Beware that optimizations provided by these plugins may break features of your theme or plugins, so you will likely need to make code changes."
+    "message": "[முக்கியமான சொத்துகளை முன்னிலைப்படுத்தவும்](https://wordpress.org/plugins/search/critical+css/) அல்லது [முக்கியத்துவம் குறைவான ஆதாரங்களைத் தவிர்க்கவும்](https://wordpress.org/plugins/search/defer+css+javascript/) உங்களுக்கு உதவக்கூடிய பல WordPress செருகுநிரல்கள் உள்ளன. இந்தச் செருகுநிரல்கள் வழங்கும் மேம்படுத்துதல்கள் உங்களின் தீம் அல்லது செருகுநிரலில் உள்ள அம்சத்தைப் பாதிக்கும். அதனால் நீங்கள் குறியீட்டில் சில மாற்றங்களைச் செய்ய வேண்டியிருக்கும்."
   },
   "stack-packs/packs/wordpress.js | time_to_first_byte": {
-    "message": "Themes, plugins, and server specifications all contribute to server response time. Consider finding a more optimized theme, carefully selecting an optimization plugin, and/or upgrading your server."
+    "message": "தீம்கள், செருகுநிரல்கள் மற்றும் சேவையக விவரக்குறிப்புகள் அனைத்தும் சேவையகத்தின் வேகத்தை நிர்ணயிக்கும். இன்னும் பல மேம்படுத்தப்பட்ட தீம்மைக் கண்டறிந்து, மேம்படுத்தும் செருகுநிரலைக் கவனமாகத் தேர்ந்தெடுத்து மற்றும்/அல்லது சேவையகத்தை மேம்படுத்தவும்."
   },
   "stack-packs/packs/wordpress.js | total_byte_weight": {
-    "message": "Consider showing excerpts in your post lists (e.g. via the more tag), reducing the number of posts shown on a given page, breaking your long posts into multiple pages, or using a plugin to lazy-load comments."
+    "message": "இடுகை பட்டியல்களில் முக்கியமான பகுதியை மட்டும் காட்டலாம் (உதாரணமாக மேலும் என்ற குறிச்சொல்லுடன்), பக்கத்தில் இடுகைகளின் எண்ணிக்கையைக் குறைக்கலாம், ஒரு பெரிய இடுகையைப் பல சின்ன பக்கங்களாகப் பிரிக்கலாம் அல்லது தேவையுள்ள போது மட்டும் கருத்துகளைச் செருகுநிரல்கள் மூலம் ஏற்றலாம்."
   },
   "stack-packs/packs/wordpress.js | unminified_css": {
-    "message": "A number of [WordPress plugins](https://wordpress.org/plugins/search/minify+css/) can speed up your site by concatenating, minifying, and compressing your styles. You may also want to use a build process to do this minification up-front if possible."
+    "message": "பல [WordPress செருகுநிரல்கள்](https://wordpress.org/plugins/search/minify+javascript/) உங்கள் இணையதளத்தில் உள்ள ஸ்டைல்களைச் சிறிதாக்கி சுருக்கி மற்றும் ஒன்றிணைத்து அதை வேகப்படுத்தலாம். ஸ்கிரிப்ட்டைச் சிறிதாக்க பதிப்பு முறைமையைப் பயன்படுத்த நேரிடலாம்."
   },
   "stack-packs/packs/wordpress.js | unminified_javascript": {
-    "message": "A number of [WordPress plugins](https://wordpress.org/plugins/search/minify+javascript/) can speed up your site by concatenating, minifying, and compressing your scripts. You may also want to use a build process to do this minification up front if possible."
+    "message": "பல [WordPress செருகுநிரல்கள்](https://wordpress.org/plugins/search/minify+javascript/) உங்கள் இணையதளத்தில் உள்ள ஸ்கிரிப்ட்டைச் சிறிதாக்கி சுருக்கி மற்றும் ஒன்றிணைத்து அதை வேகப்படுத்தலாம். ஸ்கிரிப்ட்டைச் சிறிதாக்க பதிப்பு முறைமையைப் பயன்படுத்த நேரிடலாம்."
   },
   "stack-packs/packs/wordpress.js | unused_css_rules": {
-    "message": "Consider reducing, or switching, the number of [WordPress plugins](https://wordpress.org/plugins/) loading unused CSS in your page. To identify plugins that are adding extraneous CSS, try running [code coverage](https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage) in Chrome DevTools. You can identify the theme/plugin responsible from the URL of the stylesheet. Look out for plugins that have many stylesheets in the list which have a lot of red in code coverage. A plugin should only enqueue a stylesheet if it is actually used on the page."
+    "message": "உங்கள் பக்கத்தில் [WordPress செருகுநிரல்கள்](https://wordpress.org/plugins/) ஏற்றும் பயன்படுத்தப்படாத CSSகளின் எண்ணிக்கையைக் குறைக்கவும் அல்லது மாற்றிப் பார்க்கவும். Chrome DevToolsஸில் [குறியீட்டுக் கவரேஜ்] (https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage) என்பதை இயக்கி தேவையற்ற CSSஸைச் சேர்க்கும் செருகுநிரல்களைக் கண்டறியலாம். stylesheetடின் URLலில் இதற்குக் காரணமான தீம்/செருகுநிரலைக் கண்டறியலாம். அதிகமாகச் சிகப்பில் உள்ள குறியீட்டு கவரேஜைக் கொண்ட பட்டியலில் பல stylesheetகளைக் கொண்ட செருகுநிரல்களைக் கண்டறியவும். இணையப் பக்கத்தில் பயன்படுத்தும் பட்சத்தில் ஒரு செருகுநிரல் ஒரு stylesheetடை மட்டுமே வரிசையில் சேர்க்க வேண்டும்."
   },
   "stack-packs/packs/wordpress.js | unused_javascript": {
-    "message": "Consider reducing, or switching, the number of [WordPress plugins](https://wordpress.org/plugins/) loading unused JavaScript in your page. To identify plugins that are adding extraneous JS, try running [code coverage](https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage) in Chrome DevTools. You can identify the theme/plugin responsible from the URL of the script. Look out for plugins that have many scripts in the list which have a lot of red in code coverage. A plugin should only enqueue a script if it is actually used on the page."
+    "message": "உங்கள் பக்கத்தில் [WordPress செருகுநிரல்கள்](https://wordpress.org/plugins/) ஏற்றும் பயன்படுத்தப்படாத JavaScriptகளின் எண்ணிக்கையைக் குறைக்கவும் அல்லது மாற்றிப் பார்க்கவும். Chrome DevToolsஸில் [குறியீட்டுக் கவரேஜ்](https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage) என்பதை இயக்கி தேவையற்ற JSகளைச் சேர்க்கும் செருகுநிரல்களைக் கண்டறியவும். ஸ்கிரிப்ட்டின் URLலில் இதற்குக் காரணமான தீம்/செருகுநிரலைக் கண்டறியலாம். அதிகமாகச் சிகப்பில் உள்ள குறியீட்டு கவரேஜைக் கொண்ட பட்டியலில் பல ஸ்கிரிப்ட்களைக் கொண்ட செருகுநிரல்களைக் கண்டறியவும். இணையப் பக்கத்தில் பயன்படுத்தும் பட்சத்தில் ஒரு செருகுநிரல் ஒரு ஸ்கிரிப்ட்டை மட்டுமே வரிசையில் சேர்க்க வேண்டும்."
   },
   "stack-packs/packs/wordpress.js | uses_long_cache_ttl": {
-    "message": "Read about [Browser Caching in WordPress](https://codex.wordpress.org/WordPress_Optimization#Browser_Caching)."
+    "message": "[WordPressஸில் உலாவியின் தற்காலிக சேமிப்பு] பற்றி அறிய: (https://codex.wordpress.org/WordPress_Optimization#Browser_Caching)."
   },
   "stack-packs/packs/wordpress.js | uses_optimized_images": {
-    "message": "Consider using an [image optimization WordPress plugin](https://wordpress.org/plugins/search/optimize+images/) that compresses your images while retaining quality."
+    "message": "[படங்களை மேம்படுத்தும் WordPress செருகுநிரல்](https://wordpress.org/plugins/search/optimize+images/) என்பதைப் பயன்படுத்தி உங்கள் படங்களின் தரத்தை இழக்காமல் சுருங்கச் செய்யலாம்."
   },
   "stack-packs/packs/wordpress.js | uses_responsive_images": {
-    "message": "Upload images directly through the [media library](https://codex.wordpress.org/Media_Library_Screen) to ensure that the required image sizes are available, and then insert them from the media library or use the image widget to ensure the optimal image sizes are used (including those for the responsive breakpoints). Avoid using `Full Size` images unless the dimensions are adequate for their usage. [Learn More](https://codex.wordpress.org/Inserting_Images_into_Posts_and_Pages#Image_Size)."
+    "message": "[மீடியா லைப்ரரி](https://codex.wordpress.org/Media_Library_Screen) மூலம் படத்தை நேரடியாகப் பதிவேற்றி சரியான அளவில் கிடைக்கிறதா என்று உறுதிப்படுத்தலாம், பிறகு மீடியா லைப்ரரியிலிருந்தோ பட விட்ஜெட்டைப் பயன்படுத்தியோ மேம்படுத்தப்பட்ட அளவில் படங்கள் பயன்படுத்தப்படுகிறதா என்பதை உறுதி செய்யவும் (பதிலளிக்கும் அளவு வரம்புகள் உட்பட). பயன்படுத்துவதற்கு ஏதுவான அளவுகள் உள்ளபோது மட்டுமே 'முழு அளவு' படங்களைப் பயன்படுத்தவும். [மேலும் அறிக](https://codex.wordpress.org/Inserting_Images_into_Posts_and_Pages#Image_Size)."
   },
   "stack-packs/packs/wordpress.js | uses_text_compression": {
-    "message": "You can enable text compression in your web server configuration."
+    "message": "இணையச் சேவையக உள்ளமைவில் உரை சுருக்குதலை இயக்கலாம்."
   },
   "stack-packs/packs/wordpress.js | uses_webp_images": {
-    "message": "Consider using a [plugin](https://wordpress.org/plugins/search/convert+webp/) or service that will automatically convert your uploaded images to the optimal formats."
+    "message": "நீங்கள் பதிவேற்றிய படங்களை மேம்படுத்தப்பட்ட வடிவத்திற்குத் தானாக மாற்ற ஒரு [செருகுநிரலையோ](https://wordpress.org/plugins/search/convert+webp/) சேவையையோ பயன்படுத்திப் பாருங்கள்."
   }
 }
\ No newline at end of file
diff --git a/lighthouse-core/report/html/renderer/category-renderer.js b/lighthouse-core/report/html/renderer/category-renderer.js
index e61b4b3b5..b03bdf238 100644
--- a/lighthouse-core/report/html/renderer/category-renderer.js
+++ b/lighthouse-core/report/html/renderer/category-renderer.js
@@ -128,10 +128,10 @@ class CategoryRenderer {
 
     // Add list of warnings or singular warning
     const warningsEl = this.dom.createChildOf(titleEl, 'div', 'lh-warnings');
+    this.dom.createChildOf(warningsEl, 'span').textContent = Util.UIStrings.warningHeader;
     if (warnings.length === 1) {
-      warningsEl.textContent = `${Util.UIStrings.warningHeader} ${warnings.join('')}`;
+      warningsEl.appendChild(this.dom.document().createTextNode(warnings.join('')));
     } else {
-      warningsEl.textContent = Util.UIStrings.warningHeader;
       const warningsUl = this.dom.createChildOf(warningsEl, 'ul');
       for (const warning of warnings) {
         const item = this.dom.createChildOf(warningsUl, 'li');
diff --git a/lighthouse-core/report/html/renderer/performance-category-renderer.js b/lighthouse-core/report/html/renderer/performance-category-renderer.js
index 05115ee48..943f80d69 100644
--- a/lighthouse-core/report/html/renderer/performance-category-renderer.js
+++ b/lighthouse-core/report/html/renderer/performance-category-renderer.js
@@ -127,10 +127,9 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
     const metricAuditsEl = this.renderAuditGroup(groups.metrics);
 
     // Metric descriptions toggle.
-    const auditGroupHeader = this.dom.find('.lh-audit-group__header', metricAuditsEl);
     const toggleTmpl = this.dom.cloneTemplate('#tmpl-lh-metrics-toggle', this.templateContext);
     const toggleEl = this.dom.find('.lh-metrics-toggle', toggleTmpl);
-    auditGroupHeader.appendChild(toggleEl);
+    metricAuditsEl.prepend(...toggleEl.childNodes);
 
     const keyMetrics = metricAudits.filter(a => a.weight >= 3);
     const otherMetrics = metricAudits.filter(a => a.weight < 3);
@@ -148,8 +147,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
 
     // 'Values are estimated and may vary' is used as the category description for PSI
     if (environment !== 'PSI') {
-      const estValuesEl = this.dom.createChildOf(metricsColumn2El, 'div',
-          'lh-metrics__disclaimer lh-metrics__disclaimer');
+      const estValuesEl = this.dom.createChildOf(metricsColumn1El, 'div', 'lh-metrics__disclaimer');
       estValuesEl.textContent = Util.UIStrings.varianceDisclaimer;
     }
 
diff --git a/lighthouse-core/report/html/renderer/report-ui-features.js b/lighthouse-core/report/html/renderer/report-ui-features.js
index bc252bccf..e0733f3b5 100644
--- a/lighthouse-core/report/html/renderer/report-ui-features.js
+++ b/lighthouse-core/report/html/renderer/report-ui-features.js
@@ -58,10 +58,6 @@ class ReportUIFeatures {
     this.stickyHeaderEl; // eslint-disable-line no-unused-expressions
     /** @type {HTMLElement} */
     this.highlightEl; // eslint-disable-line no-unused-expressions
-    /** @type {HTMLInputElement} */
-    this.metricDescriptionToggleEl; // eslint-disable-line no-unused-expressions
-    /** @type {HTMLElement} */
-    this.metricAuditGroup; // eslint-disable-line no-unused-expressions
 
     this.onMediaQueryChange = this.onMediaQueryChange.bind(this);
     this.onCopy = this.onCopy.bind(this);
@@ -74,7 +70,6 @@ class ReportUIFeatures {
     this.expandAllDetails = this.expandAllDetails.bind(this);
     this._toggleDarkTheme = this._toggleDarkTheme.bind(this);
     this._updateStickyHeaderOnScroll = this._updateStickyHeaderOnScroll.bind(this);
-    this._toggleMetricDescription = this._toggleMetricDescription.bind(this);
   }
 
   /**
@@ -87,19 +82,21 @@ class ReportUIFeatures {
 
     this.json = report;
     this._setupMediaQueryListeners();
-    this._setupSmoothScroll();
     this._setupExportButton();
     this._setupThirdPartyFilter();
-    this._setupStickyHeaderElements();
     this._setUpCollapseDetailsAfterPrinting();
     this._resetUIState();
     this._document.addEventListener('keyup', this.onKeyUp);
     this._document.addEventListener('copy', this.onCopy);
-    this._document.addEventListener('scroll', this._updateStickyHeaderOnScroll);
-    window.addEventListener('resize', this._updateStickyHeaderOnScroll);
-    this._setupMetricDescriptionToggleElements();
     const topbarLogo = this._dom.find('.lh-topbar__logo', this._document);
     topbarLogo.addEventListener('click', this._toggleDarkTheme);
+
+    // There is only a sticky header when at least 2 categories are present.
+    if (Object.keys(this.json.categories).length >= 2) {
+      this._setupStickyHeaderElements();
+      this._document.addEventListener('scroll', this._updateStickyHeaderOnScroll);
+      window.addEventListener('resize', this._updateStickyHeaderOnScroll);
+    }
   }
 
   /**
@@ -120,17 +117,6 @@ class ReportUIFeatures {
     this.onMediaQueryChange(mediaQuery);
   }
 
-  _setupSmoothScroll() {
-    for (const el of this._dom.findAll('a.lh-gauge__wrapper', this._document)) {
-      const anchorElement = /** @type {HTMLAnchorElement} */ (el);
-      anchorElement.addEventListener('click', e => {
-        e.preventDefault();
-        window.history.pushState({}, '', anchorElement.hash);
-        this._dom.find(anchorElement.hash, this._document).scrollIntoView({behavior: 'smooth'});
-      });
-    }
-  }
-
   /**
    * Handle media query change events.
    * @param {MediaQueryList|MediaQueryListEvent} mql
@@ -260,26 +246,6 @@ class ReportUIFeatures {
     this._copyAttempt = false;
   }
 
-  _setupMetricDescriptionToggleElements() {
-    const metricDescriptionToggleEl = this._document.querySelector('.lh-metrics-toggle__input');
-    // No metrics if performance category wasn't run.
-    if (!metricDescriptionToggleEl) return;
-
-    this.metricDescriptionToggleEl = /** @type {HTMLInputElement} */ (metricDescriptionToggleEl);
-    this.metricAuditGroup = this._dom.find('.lh-audit-group--metrics', this._document);
-    this.metricDescriptionToggleEl.addEventListener('input', this._toggleMetricDescription);
-    this.metricAuditGroup.addEventListener('click', e => {
-      const el = /** @type {HTMLElement} */ (e.target);
-      if (el.closest('.lh-metric__title')) this.metricDescriptionToggleEl.click();
-    });
-  }
-
-  _toggleMetricDescription() {
-    this.metricDescriptionToggleEl.blur();
-    const show = this.metricDescriptionToggleEl.checked;
-    this.metricAuditGroup.classList.toggle('lh-audit-group--metrics__show-descriptions', show);
-  }
-
   /**
    * Copies the report JSON to the clipboard (if supported by the browser).
    */
@@ -396,6 +362,10 @@ class ReportUIFeatures {
         this.saveAsGist();
         break;
       }
+      case 'toggle-dark': {
+        this._toggleDarkTheme();
+        break;
+      }
     }
 
     this.closeExportDropdown();
diff --git a/lighthouse-core/report/html/report-styles.css b/lighthouse-core/report/html/report-styles.css
index 996e03478..39f353415 100644
--- a/lighthouse-core/report/html/report-styles.css
+++ b/lighthouse-core/report/html/report-styles.css
@@ -76,6 +76,7 @@
   --lh-audit-group-vpadding: 8px;
   --lh-section-vpadding: 12px;
   --chevron-size: 12px;
+  --inner-audit-left-padding: calc(var(--score-shape-size) + var(--score-shape-margin-left) + var(--score-shape-margin-right));
 
   /* Palette. */
   --color-black-100: #F5F5F5;
@@ -85,6 +86,7 @@
   --color-black-600: #757575;
   --color-black-800: #424242;
   --color-black-900: #212121;
+  --off-black: #111111;
   --color-black: #000000;
   --color-blue: #2962FF;
   --color-green-700: #018642;
@@ -104,8 +106,8 @@
   --env-name-min-width: 220px;
   --env-tem-padding: 10px 0px;
   --expandable-padding: 0 0 2px calc(var(--score-shape-margin-left) + var(--score-shape-size) + var(--score-shape-margin-right));
-  --gauge-circle-size-big: 120px;
-  --gauge-circle-size: 96px;
+  --gauge-circle-size-big: 112px;
+  --gauge-circle-size: 80px;
   --header-padding: 20px 0 20px 0;
   --highlighter-bg: var(--color-black-400);
   --icon-square-size: calc(var(--score-shape-size) * 0.88);
@@ -115,14 +117,13 @@
   --plugin-icon-size: 65%;
   --pwa-icon-margin: 0 6px 0 -2px;
   --pwa-icon-size: var(--topbar-icon-size);
-  --score-container-padding: 12px;
-  --score-container-width: 160px;
-  --score-number-font-size-big: 42px;
-  --score-number-font-size: 34px;
+  --score-container-padding: 8px;
+  --score-container-width: 148px;
+  --score-number-font-size-big: 38px;
+  --score-number-font-size: 28px;
   --score-shape-margin-left: 4px;
   --score-shape-margin-right: 12px;
-  --score-shape-margin-top: 7px;
-  --score-shape-margin: var(--score-shape-margin-top) var(--score-shape-margin-right) 0 var(--score-shape-margin-left);
+  --score-shape-margin: 0 var(--score-shape-margin-right) 0 var(--score-shape-margin-left);
   --score-shape-size: 12px;
   --score-title-font-size-big: 28px;
   --score-title-font-size: 20px;
@@ -132,9 +133,10 @@
   --scorescale-width: 18px;
   --section-padding: 40px;
   --topbar-bg: var(--color-black-100);
-  --topbar-height: 36px;
+  --topbar-height: 32px;
   --topbar-icon-size: 24px;
   --topbar-padding: 0 8px;
+  --metrics-toggle-color: var(--color-black-200);
 
   --color-average-secondary: var(--color-orange-700);
   --color-average: var(--color-orange);
@@ -180,7 +182,8 @@
   --topbar-bg: var(--color-black);
   --plugin-badge-bg: var(--color-black-800);
   --header-bg-color: var(--color-black-900);
-  --env-item-bg: rgba(0, 0, 0, 0.5);
+  --env-item-bg: var(--color-black);
+  --report-secondary-border-color: var(--color-black-200);
 
   --body-background-color: var(--color-black-900);
   --body-text-color: var(--color-black-100);
@@ -212,19 +215,17 @@
     --header-padding: 16px 0 16px 0;
     --plugin-icon-size: 75%;
     --pwa-icon-margin: 0 7px 0 -3px;
-    --score-container-padding: 8px;
     --score-container-width: 112px;
     --score-number-font-size-big: 34px;
     --score-number-font-size: 26px;
     --score-shape-margin-left: 2px;
-    --score-shape-margin-top: 7px;
     --score-shape-size: 10px;
     --score-title-font-size-big: 22px;
     --score-title-font-size: 14px;
     --score-title-line-height-big: 26px;
     --score-title-line-height: 20px;
     --section-padding: 24px;
-    --topbar-height: 32px;
+    --topbar-height: 28px;
     --topbar-icon-size: 20px;
   }
 
@@ -325,7 +326,6 @@
 }
 
 .lh-audit__description, .lh-audit__stackpack {
-  --inner-audit-left-padding: calc(var(--text-indent) + var(--lh-audit-index-width) + 2 * var(--audit-item-gap));
   --inner-audit-right-padding: calc(var(--text-indent) + 2px);
   padding-left: var(--inner-audit-left-padding);
   padding-right: var(--inner-audit-right-padding);
@@ -337,6 +337,7 @@
   font-size: var(--body-font-size);
   margin-top: var(--default-padding);
   margin-bottom: var(--default-padding);
+  margin-left: var(--inner-audit-left-padding);
   /* whatever the .lh-details side margins are */
   width: 100%;
 }
@@ -383,21 +384,8 @@
 .report-icon--download {
   background-image: url('data:image/svg+xml;utf8,<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>');
 }
-
-.dark .report-icon--share {
-  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="white" d="M0 0h24v24H0z"/><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z"/></svg>');
-}
-.dark .report-icon--print {
-  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="white" d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"/><path fill="none" d="M0 0h24v24H0z"/></svg>');
-}
-.dark .report-icon--copy {
-  background-image: url('data:image/svg+xml;utf8,<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path fill="none" d="M0 0h24v24H0z" /><path fill="white" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>');
-}
-.dark .report-icon--open {
-  background-image: url('data:image/svg+xml;utf8,<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path fill="none" d="M0 0h24v24H0z" /><path fill="white" d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h4v-2H5V8h14v10h-4v2h4c1.1 0 2-.9 2-2V6c0-1.1-.89-2-2-2zm-7 6l-4 4h3v6h2v-6h3l-4-4z"/></svg>');
-}
-.dark .report-icon--download {
-  background-image: url('data:image/svg+xml;utf8,<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path fill="white" d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>');
+.report-icon--dark {
+  background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 100 125"><path d="M50 23.587c-16.27 0-22.799 12.574-22.799 21.417 0 12.917 10.117 22.451 12.436 32.471h20.726c2.32-10.02 12.436-19.554 12.436-32.471 0-8.843-6.528-21.417-22.799-21.417zM39.637 87.161c0 3.001 1.18 4.181 4.181 4.181h.426l.41 1.231C45.278 94.449 46.042 95 48.019 95h3.963c1.978 0 2.74-.551 3.365-2.427l.409-1.231h.427c3.002 0 4.18-1.18 4.18-4.181V80.91H39.637v6.251zM50 18.265c1.26 0 2.072-.814 2.072-2.073v-9.12C52.072 5.813 51.26 5 50 5c-1.259 0-2.072.813-2.072 2.073v9.12c0 1.259.813 2.072 2.072 2.072zM68.313 23.727c.994.774 2.135.634 2.91-.357l5.614-7.187c.776-.992.636-2.135-.356-2.909-.992-.776-2.135-.636-2.91.357l-5.613 7.186c-.778.993-.636 2.135.355 2.91zM91.157 36.373c-.306-1.222-1.291-1.815-2.513-1.51l-8.85 2.207c-1.222.305-1.814 1.29-1.51 2.512.305 1.223 1.291 1.814 2.513 1.51l8.849-2.206c1.223-.305 1.816-1.291 1.511-2.513zM86.757 60.48l-8.331-3.709c-1.15-.512-2.225-.099-2.736 1.052-.512 1.151-.1 2.224 1.051 2.737l8.33 3.707c1.15.514 2.225.101 2.736-1.05.513-1.149.1-2.223-1.05-2.737zM28.779 23.37c.775.992 1.917 1.131 2.909.357.992-.776 1.132-1.917.357-2.91l-5.615-7.186c-.775-.992-1.917-1.132-2.909-.357s-1.131 1.917-.356 2.909l5.614 7.187zM21.715 39.583c.305-1.223-.288-2.208-1.51-2.513l-8.849-2.207c-1.222-.303-2.208.289-2.513 1.511-.303 1.222.288 2.207 1.511 2.512l8.848 2.206c1.222.304 2.208-.287 2.513-1.509zM21.575 56.771l-8.331 3.711c-1.151.511-1.563 1.586-1.05 2.735.511 1.151 1.586 1.563 2.736 1.052l8.331-3.711c1.151-.511 1.563-1.586 1.05-2.735-.512-1.15-1.585-1.562-2.736-1.052z"/></svg>');
 }
 
 /* Node */
@@ -471,6 +459,7 @@
 }
 .lh-category-header__description  {
   font-size: var(--body-font-size);
+  text-align: center;
   margin: 0px auto;
   max-width: 400px;
 }
@@ -501,6 +490,7 @@
 /* Expandable Details (Audit Groups, Audits) */
 .lh-audit__header {
   display: flex;
+  align-items: center;
   font-weight: 500;
   padding: var(--lh-audit-vpadding) 0;
 }
@@ -573,9 +563,10 @@
 
 .lh-metric__innerwrap {
   display: flex;
+  align-items: center;
   flex-wrap: wrap;
   justify-content: space-between;
-  padding: 8px var(--text-indent);
+  padding: 10px var(--text-indent);
 }
 
 .lh-metric__details {
@@ -589,7 +580,6 @@
 
 .lh-metrics__disclaimer {
   color: var(--medium-75-gray);
-  text-align: right;
   margin: var(--lh-section-vpadding) 0;
   padding: 0 var(--text-indent);
 }
@@ -599,30 +589,36 @@
   color: var(--secondary-text-color);
   padding: var(--expandable-padding);
 }
-.lh-audit-group--metrics__show-descriptions .lh-metric__description {
-  display: block;
-}
 
 .lh-metric__value {
   white-space: nowrap; /* No wrapping between metric value and the icon */
   font-weight: 500;
 }
 
-.lh-metrics-toggle {
-  position: relative;
+/* No-JS toggle switch */
+/* Keep this selector sync'd w/ `magicSelector` in report-ui-features-test.js */
+ .lh-metrics-toggle__input:checked ~ .lh-columns .lh-metric__description {
+  display: block;
 }
+
 .lh-metrics-toggle__input {
   cursor: pointer;
   opacity: 0;
   position: absolute;
-  width: 100%;
-  height: 100%;
+  right: 0;
+  width: 74px;
+  height: 28px;
+  top: -3px;
 }
-.lh-metrics-toggle label {
+.lh-metrics-toggle__label {
   display: flex;
   background-color: #eee;
   border-radius: 20px;
   overflow: hidden;
+  position: absolute;
+  right: 0;
+  top: -3px;
+  pointer-events: none;
 }
 .lh-metrics-toggle__input:focus + label {
   outline: -webkit-focus-ring-color auto 3px;
@@ -643,10 +639,15 @@
 .lh-metrics-toggle__lines {
   fill: var(--color-metric-toggle-lines);
 }
-.lh-metrics-toggle label .lh-metrics-toggle__icon--less {
+
+.lh-metrics-toggle__label  {
+  background-color: var(--metrics-toggle-color);
+}
+
+.lh-metrics-toggle__label .lh-metrics-toggle__icon--less {
   padding-left: 8px;
 }
-.lh-metrics-toggle label .lh-metrics-toggle__icon--more {
+.lh-metrics-toggle__label .lh-metrics-toggle__icon--more {
   padding-right: 8px;
 }
 
@@ -710,9 +711,7 @@
 }
 
 .lh-load-opportunity__header .lh-load-opportunity__col {
-  background-color: var(--medium-50-gray);
   color: var(--medium-75-gray);
-  text-align: center;
   display: unset;
   line-height: calc(2.3 * var(--body-font-size));
 }
@@ -723,10 +722,12 @@
 
 .lh-load-opportunity__col--one {
   flex: 5;
+  align-items: center;
   margin-right: 2px;
 }
 .lh-load-opportunity__col--two {
   flex: 4;
+  text-align: right;
 }
 
 .lh-audit--load-opportunity .lh-audit__display-text {
@@ -825,6 +826,7 @@
 
 .lh-audit-group {
   margin-bottom: var(--audits-margin-bottom);
+  position: relative;
 }
 
 .lh-audit-group__header::before {
@@ -886,6 +888,10 @@
   /* When the header takes 100% width, the chevron becomes small. */
   max-width: calc(100% - var(--chevron-size));
 }
+/* max-width makes the metric toggle not flush. metrics doesn't have a chevron so unset. */
+.lh-audit-group--metrics .lh-audit-group__header {
+  max-width: unset;
+}
 
 .lh-audit-group__header span.lh-audit-group__title {
   font-weight: bold;
@@ -968,11 +974,13 @@
 
 .lh-warnings {
   --item-margin: calc(var(--body-line-height) / 6);
-  border: 1px solid var(--color-average);
-  border-radius: 4px;
+  color: var(--color-average);
   margin: var(--lh-audit-vpadding) 0;
   padding: calc(var(--lh-audit-vpadding) / 2) var(--lh-audit-vpadding);
 }
+.lh-warnings span {
+  font-weight: bold;
+}
 
 .lh-warnings--toplevel {
   --item-margin: calc(var(--header-line-height) / 4);
@@ -1158,18 +1166,18 @@
 .lh-table {
   --image-preview-size: 24px;
   border-collapse: collapse;
+  /* Can't assign padding to table, so shorten the width instead. */
+  width: calc(100% - var(--inner-audit-left-padding));
 }
 
-.lh-table thead {
-  background-color: var(--lh-table-higlight-bg);
-}
 .lh-table thead th {
   font-weight: normal;
+  color: var(--color-black-600);
   /* See text-wrapping comment on .lh-container. */
   word-break: normal;
 }
 
-.lh-table tbody tr:nth-child(even) {
+.lh-table tbody tr:nth-child(odd) {
   background-color: var(--lh-table-higlight-bg);
 }
 
@@ -1177,6 +1185,12 @@
 .lh-table td {
   padding: 8px 6px;
 }
+.lh-table th:first-child {
+  padding-left: 0;
+}
+.lh-table th:last-child {
+  padding-right: 0;
+}
 
 /* Looks unnecessary, but mostly for keeping the <th>s left-aligned */
 .lh-table-column--text,
diff --git a/lighthouse-core/report/html/templates.html b/lighthouse-core/report/html/templates.html
index 06160ed05..cc96aee34 100644
--- a/lighthouse-core/report/html/templates.html
+++ b/lighthouse-core/report/html/templates.html
@@ -72,7 +72,7 @@ limitations under the License.
 <template id="tmpl-lh-metrics-toggle">
   <div class="lh-metrics-toggle">
     <input class="lh-metrics-toggle__input" type="checkbox" name="toggle-metric-descriptions">
-    <label for="toggle-metric-descriptions">
+    <label class="lh-metrics-toggle__label" for="toggle-metric-descriptions">
       <div class="lh-metrics-toggle__icon lh-metrics-toggle__icon--less" aria-hidden="true">
         <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24">
           <path class="lh-metrics-toggle__lines" d="M4 9h16v2H4zm0 4h10v2H4z" />
@@ -180,8 +180,6 @@ limitations under the License.
       justify-content: center;
       background-color: var(--color-sticky-header-bg);
       border-bottom: 1px solid var(--color-black-200);
-      padding-top: var(--score-container-padding);
-      padding-bottom: 4px;
       z-index: 1000;
       pointer-events: none;
       opacity: 0;
@@ -234,6 +232,7 @@ limitations under the License.
     .lh-topbar__logo {
       width: var(--topbar-icon-size);
       height: var(--topbar-icon-size);
+      user-select: none;
     }
     .lh-topbar__logo .shape {
       fill: var(--body-text-color);
@@ -267,7 +266,7 @@ limitations under the License.
     }
     .lh-export__button.active + .lh-export__dropdown {
       opacity: 1;
-      clip: rect(0, 164px, 210px, 0);
+      clip: rect(-1px, 187px, 242px, -3px);
     }
     .lh-export__dropdown {
       position: absolute;
@@ -305,6 +304,14 @@ limitations under the License.
       background-color: transparent;
       text-indent: 18px;
     }
+    .dark .report-icon {
+      color: var(--color-black-900);
+      filter: invert(1);
+    }
+    .dark .lh-export__dropdown a:hover,
+    .dark .lh-export__dropdown a:focus {
+      background-color: #BDBDBD;
+    }
     /* copy icon needs slight adjustments to look great */
     .lh-export__dropdown .report-icon--copy {
       background-size: 16px;
@@ -339,7 +346,7 @@ limitations under the License.
     </svg>
 
     <a href="" class="lh-topbar__url" target="_blank" rel="noopener"></a>
-    
+
     <div class="lh-export">
       <button class="report-icon report-icon--share lh-export__button" title="Export report"></button>
       <div class="lh-export__dropdown">
@@ -351,6 +358,7 @@ limitations under the License.
         <a href="#" class="report-icon report-icon--download" data-action="save-json">Save as JSON</a>
         <a href="#" class="report-icon report-icon--open lh-export--viewer" data-action="open-viewer">Open in Viewer</a>
         <a href="#" class="report-icon report-icon--open lh-export--gist" data-action="save-gist">Save as Gist</a>
+        <a href="#" class="report-icon report-icon--dark" data-action="toggle-dark">Toggle Dark Theme</a>
       </div>
     </div>
   </div>
@@ -698,7 +706,7 @@ limitations under the License.
       display: inline;
     }
     /* Just optimized. Same n/a line as no passing groups. */
-    .lh-gauge--pwa__wrapper.lh-badged--pwa-optimized .lh-gauge--pwa__na-line {
+    .lh-gauge--pwa__wrapper.lh-badged--pwa-optimized:not(.lh-badged--pwa-installable):not(.lh-badged--pwa-fast-reliable) .lh-gauge--pwa__na-line {
       display: inline;
     }
 
diff --git a/lighthouse-core/scripts/assert-golden-lhr-unchanged.sh b/lighthouse-core/scripts/assert-golden-lhr-unchanged.sh
index 7f2908446..c407e460c 100644
--- a/lighthouse-core/scripts/assert-golden-lhr-unchanged.sh
+++ b/lighthouse-core/scripts/assert-golden-lhr-unchanged.sh
@@ -28,7 +28,7 @@ trap teardown EXIT
 
 colorText "Generating a fresh LHR..." "$purple"
 set -x
-node "$lhroot_path/lighthouse-cli" -A="$lhroot_path/lighthouse-core/test/results/artifacts" --throttling-method=devtools --quiet --output=json --output-path="$freshLHRPath"
+node "$lhroot_path/lighthouse-cli" -A="$lhroot_path/lighthouse-core/test/results/artifacts" --config-path="$lhroot_path/lighthouse-core/test/results/sample-config.js" --quiet --output=json --output-path="$freshLHRPath"
 set +x
 
 # remove timing from both
diff --git a/lighthouse-core/scripts/update-report-fixtures.js b/lighthouse-core/scripts/update-report-fixtures.js
index 300941ab6..10d64051c 100644
--- a/lighthouse-core/scripts/update-report-fixtures.js
+++ b/lighthouse-core/scripts/update-report-fixtures.js
@@ -8,32 +8,13 @@
 const cli = require('../../lighthouse-cli/run.js');
 const cliFlags = require('../../lighthouse-cli/cli-flags.js');
 const assetSaver = require('../lib/asset-saver.js');
-
 const artifactPath = 'lighthouse-core/test/results/artifacts';
 
 const {server} = require('../../lighthouse-cli/test/fixtures/static-server.js');
+const budgetedConfig = require('../test/results/sample-config.js');
 
 /** @typedef {import('net').AddressInfo} AddressInfo */
 
-/** @type {LH.Config.Json} */
-const budgetedConfig = {
-  extends: 'lighthouse:default',
-  settings: {
-    budgets: [{
-      resourceSizes: [
-        {resourceType: 'script', budget: 125},
-        {resourceType: 'total', budget: 500},
-      ],
-      timings: [
-        {metric: 'interactive', budget: 5000, tolerance: 1000},
-      ],
-      resourceCounts: [
-        {resourceType: 'third-party', budget: 0},
-      ],
-    }],
-  },
-};
-
 /**
  * Update the report artifacts. If artifactName is set only that artifact will be updated.
  * @param {keyof LH.Artifacts=} artifactName
@@ -52,7 +33,6 @@ async function update(artifactName) {
   const url = `http://localhost:${port}/dobetterweb/dbw_tester.html`;
   const rawFlags = [
     `--gather-mode=${artifactPath}`,
-    '--throttling-method=devtools',
     url,
   ].join(' ');
   const flags = cliFlags.getFlags(rawFlags);
@@ -63,7 +43,7 @@ async function update(artifactName) {
     // Revert everything except the one artifact
     const newArtifacts = await assetSaver.loadArtifacts(artifactPath);
     if (!(artifactName in newArtifacts) && !(artifactName in oldArtifacts)) {
-      console.warn(`❌ Unknown artifact name: '${artifactName}'. Reverting artifacts...`); // eslint-disable-line no-console
+      throw Error('Unknown artifact name: ' + artifactName);
     }
     const finalArtifacts = oldArtifacts;
     finalArtifacts[artifactName] = newArtifacts[artifactName];
diff --git a/package.json b/package.json
index 392f9dfaa..52098cfe8 100644
--- a/package.json
+++ b/package.json
@@ -54,12 +54,12 @@
     "i18n:checks": "./lighthouse-core/scripts/i18n/assert-strings-collected.sh",
     "i18n:collect-strings": "node lighthouse-core/scripts/i18n/collect-strings.js",
     "update:sample-artifacts": "node lighthouse-core/scripts/update-report-fixtures.js",
-    "update:sample-json": "yarn i18n:collect-strings && node ./lighthouse-cli -A=./lighthouse-core/test/results/artifacts --throttling-method=devtools --output=json --output-path=./lighthouse-core/test/results/sample_v2.json && node lighthouse-core/scripts/cleanup-LHR-for-diff.js ./lighthouse-core/test/results/sample_v2.json --only-remove-timing && yarn compile-proto && yarn build-proto-roundtrip",
+    "update:sample-json": "yarn i18n:collect-strings && node ./lighthouse-cli -A=./lighthouse-core/test/results/artifacts --config-path=./lighthouse-core/test/results/sample-config.js --output=json --output-path=./lighthouse-core/test/results/sample_v2.json && node lighthouse-core/scripts/cleanup-LHR-for-diff.js ./lighthouse-core/test/results/sample_v2.json --only-remove-timing && yarn compile-proto && yarn build-proto-roundtrip",
     "diff:sample-json": "yarn i18n:checks && bash lighthouse-core/scripts/assert-golden-lhr-unchanged.sh",
     "ultradumbBenchmark": "./lighthouse-core/scripts/benchmark.js",
     "mixed-content": "./lighthouse-cli/index.js --chrome-flags='--headless' --preset=mixed-content",
     "minify-latest-run": "./lighthouse-core/scripts/lantern/minify-trace.js ./latest-run/defaultPass.trace.json ./latest-run/defaultPass.trace.min.json && ./lighthouse-core/scripts/lantern/minify-devtoolslog.js ./latest-run/defaultPass.devtoolslog.json ./latest-run/defaultPass.devtoolslog.min.json",
-    "compile-proto": "protoc --python_out=./ ./proto/lighthouse-result.proto && mv ./proto/*_pb2.py ./proto/scripts || (echo \"❌ Install protobuf to compile the proto file.\" && false)",
+    "compile-proto": "protoc --python_out=./ ./proto/lighthouse-result.proto && mv ./proto/*_pb2.py ./proto/scripts || (echo \"❌ Install protobuf ≥ 3.7.1 to compile the proto file.\" && false)",
     "build-proto-roundtrip": "cd proto/scripts && PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION=2 python json_roundtrip_via_proto.py"
   },
   "devDependencies": {
diff --git a/proto/sample_v2_round_trip.json b/proto/sample_v2_round_trip.json
index d9f5ba95c..c0c4c3b91 100644
--- a/proto/sample_v2_round_trip.json
+++ b/proto/sample_v2_round_trip.json
@@ -20,6 +20,14 @@
             "scoreDisplayMode": "binary",
             "title": "Uses Application Cache"
         },
+        "apple-touch-icon": {
+            "description": "For ideal appearance on iOS when users add to the home screen, define an apple-touch-icon. It must point to a non-transparent 192px (or 180px) square PNG. [Learn More](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/).",
+            "id": "apple-touch-icon",
+            "score": 0.0,
+            "scoreDisplayMode": "binary",
+            "title": "Does not provide a valid `apple-touch-icon`",
+            "warnings": []
+        },
         "aria-allowed-attr": {
             "description": "Each ARIA `role` supports a specific subset of `aria-*` attributes. Mismatching these invalidates the `aria-*` attributes. [Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-allowed-attr?application=lighthouse).",
             "id": "aria-allowed-attr",
@@ -2032,10 +2040,105 @@
             "title": "Prevents users to paste into password fields"
         },
         "performance-budget": {
-            "description": "Keep the quantity and size of network requests under the targets set by the provided performance budget.",
+            "description": "Keep the quantity and size of network requests under the targets set by the provided performance budget. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).",
+            "details": {
+                "headings": [
+                    {
+                        "itemType": "text",
+                        "key": "label",
+                        "text": "Resource Type"
+                    },
+                    {
+                        "itemType": "numeric",
+                        "key": "requestCount",
+                        "text": "Requests"
+                    },
+                    {
+                        "itemType": "bytes",
+                        "key": "size",
+                        "text": "Transfer Size"
+                    },
+                    {
+                        "itemType": "text",
+                        "key": "countOverBudget"
+                    },
+                    {
+                        "itemType": "bytes",
+                        "key": "sizeOverBudget",
+                        "text": "Over Budget"
+                    }
+                ],
+                "items": [
+                    {
+                        "countOverBudget": "2 requests",
+                        "label": "Script",
+                        "requestCount": 4.0,
+                        "resourceType": "script",
+                        "size": 103675.0,
+                        "sizeOverBudget": 72955.0
+                    },
+                    {
+                        "countOverBudget": "8 requests",
+                        "label": "Total",
+                        "requestCount": 18.0,
+                        "resourceType": "total",
+                        "size": 160738.0,
+                        "sizeOverBudget": 58338.0
+                    },
+                    {
+                        "label": "Other",
+                        "requestCount": 2.0,
+                        "resourceType": "other",
+                        "size": 12861.0,
+                        "sizeOverBudget": 7741.0
+                    },
+                    {
+                        "countOverBudget": "1 request",
+                        "label": "Third-party",
+                        "requestCount": 2.0,
+                        "resourceType": "third-party",
+                        "size": 30174.0,
+                        "sizeOverBudget": 4574.0
+                    },
+                    {
+                        "countOverBudget": "5 requests",
+                        "label": "Stylesheet",
+                        "requestCount": 7.0,
+                        "resourceType": "stylesheet",
+                        "size": 5352.0,
+                        "sizeOverBudget": 232.0
+                    },
+                    {
+                        "label": "Image",
+                        "requestCount": 2.0,
+                        "resourceType": "image",
+                        "size": 24741.0
+                    },
+                    {
+                        "label": "Media",
+                        "requestCount": 0.0,
+                        "resourceType": "media",
+                        "size": 0.0
+                    },
+                    {
+                        "label": "Font",
+                        "requestCount": 0.0,
+                        "resourceType": "font",
+                        "size": 0.0
+                    },
+                    {
+                        "countOverBudget": "2 requests",
+                        "label": "Document",
+                        "requestCount": 3.0,
+                        "resourceType": "document",
+                        "size": 14109.0
+                    }
+                ],
+                "type": "table"
+            },
             "id": "performance-budget",
             "score": null,
-            "scoreDisplayMode": "notApplicable",
+            "scoreDisplayMode": "informative",
             "title": "Performance budget"
         },
         "plugins": {
@@ -2150,7 +2253,7 @@
             "title": "Eliminate render-blocking resources"
         },
         "resource-summary": {
-            "description": "To set budgets for the quantity and size of page resources, add a budget.json file.",
+            "description": "To set budgets for the quantity and size of page resources, add a budget.json file. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).",
             "details": {
                 "headings": [
                     {
@@ -2356,15 +2459,52 @@
         "tap-targets": {
             "description": "Interactive elements like buttons and links should be large enough (48x48px), and have enough space around them, to be easy enough to tap without overlapping onto other elements. [Learn more](https://developers.google.com/web/fundamentals/accessibility/accessible-styles#multi-device_responsive_design).",
             "details": {
-                "headings": [],
-                "items": [],
+                "headings": [
+                    {
+                        "itemType": "node",
+                        "key": "tapTarget",
+                        "text": "Tap Target"
+                    },
+                    {
+                        "itemType": "text",
+                        "key": "size",
+                        "text": "Size"
+                    },
+                    {
+                        "itemType": "node",
+                        "key": "overlappingTarget",
+                        "text": "Overlapping Target"
+                    }
+                ],
+                "items": [
+                    {
+                        "height": 18.0,
+                        "overlapScoreRatio": 0.8333333333333334,
+                        "overlappingTarget": {
+                            "path": "3,HTML,1,BODY,18,BUTTON",
+                            "selector": "body > button.small-button",
+                            "snippet": "<button class=\"small-button\">Do something else</button>",
+                            "type": "node"
+                        },
+                        "overlappingTargetScore": 720.0,
+                        "size": "200x18",
+                        "tapTarget": {
+                            "path": "3,HTML,1,BODY,17,BUTTON",
+                            "selector": "body > button.small-button",
+                            "snippet": "<button class=\"small-button\">Do something</button>",
+                            "type": "node"
+                        },
+                        "tapTargetScore": 864.0,
+                        "width": 200.0
+                    }
+                ],
                 "type": "table"
             },
-            "displayValue": "100% appropriately sized tap targets",
+            "displayValue": "0% appropriately sized tap targets",
             "id": "tap-targets",
-            "score": 1.0,
+            "score": 0.0,
             "scoreDisplayMode": "binary",
-            "title": "Tap targets are sized appropriately"
+            "title": "Tap targets are not sized appropriately"
         },
         "td-headers-attr": {
             "description": "Screen readers have features to make navigating tables easier. Ensuring `<td>` cells using the `[headers]` attribute only refer to other cells in the same table may improve the experience for screen reader users. [Learn more](https://dequeuniversity.com/rules/axe/3.1/td-headers-attr?application=lighthouse).",
@@ -3551,6 +3691,11 @@
                     "id": "without-javascript",
                     "weight": 1.0
                 },
+                {
+                    "group": "pwa-optimized",
+                    "id": "apple-touch-icon",
+                    "weight": 1.0
+                },
                 {
                     "id": "pwa-cross-browser",
                     "weight": 0.0
@@ -3567,7 +3712,7 @@
             "description": "These checks validate the aspects of a Progressive Web App. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist).",
             "id": "pwa",
             "manualDescription": "These checks are required by the baseline [PWA Checklist](https://developers.google.com/web/progressive-web-apps/checklist) but are not automatically checked by Lighthouse. They do not affect your score but it's important that you verify them manually.",
-            "score": 0.42,
+            "score": 0.41,
             "title": "Progressive Web App"
         },
         "seo": {
@@ -3645,7 +3790,7 @@
             "description": "These checks ensure that your page is optimized for search engine results ranking. There are additional factors Lighthouse does not check that may affect your search ranking. [Learn more](https://support.google.com/webmasters/answer/35769).",
             "id": "seo",
             "manualDescription": "Run these additional validators on your site to check additional SEO best practices.",
-            "score": 0.82,
+            "score": 0.73,
             "title": "SEO"
         }
     },
@@ -4076,6 +4221,12 @@
                 "name": "lh:computed:ManifestValues",
                 "startTime": 0.0
             },
+            {
+                "duration": 100.0,
+                "entryType": "measure",
+                "name": "lh:audit:apple-touch-icon",
+                "startTime": 0.0
+            },
             {
                 "duration": 100.0,
                 "entryType": "measure",


Test patch:
diff --git a/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap b/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap
index d1e45ec5d..13268b7bc 100644
--- a/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap
+++ b/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap
@@ -69,6 +69,9 @@ Object {
     Object {
       "path": "installable-manifest",
     },
+    Object {
+      "path": "apple-touch-icon",
+    },
     Object {
       "path": "splash-screen",
     },
@@ -923,6 +926,11 @@ Object {
           "id": "without-javascript",
           "weight": 1,
         },
+        Object {
+          "group": "pwa-optimized",
+          "id": "apple-touch-icon",
+          "weight": 1,
+        },
         Object {
           "id": "pwa-cross-browser",
           "weight": 0,
diff --git a/lighthouse-cli/test/fixtures/dobetterweb/dbw_tester.html b/lighthouse-cli/test/fixtures/dobetterweb/dbw_tester.html
index a5fb28e31..5e0042bd4 100644
--- a/lighthouse-cli/test/fixtures/dobetterweb/dbw_tester.html
+++ b/lighthouse-cli/test/fixtures/dobetterweb/dbw_tester.html
@@ -182,6 +182,16 @@
 <!-- FAIL(efficient-animated-content): animated gif found -->
 <img src="lighthouse-rotating.gif" width="811" height="462">
 
+<!-- FAIL(tap-targets): buttons too close together -->
+<style>
+.small-button {
+  display: block;
+  width: 200px;
+}
+</style>
+<button class="small-button">Do something</button>
+<button class="small-button">Do something else</button>
+
 <!-- Some websites overwrite the original Error object. The captureJSCallUsage function
   relies on the native Error object and prepareStackTrace from V8. When overwriting the stack
   property the e.stack inside the gatherer won't be in the correct format
diff --git a/lighthouse-cli/test/fixtures/perf/perf-budgets/big-script.js b/lighthouse-cli/test/fixtures/perf/perf-budgets/big-script.js
new file mode 100644
index 000000000..02944af7f
--- /dev/null
+++ b/lighthouse-cli/test/fixtures/perf/perf-budgets/big-script.js
@@ -0,0 +1,72 @@
+/**
+ * @license Copyright 2019 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
+ */
+/* eslint-disable */
+
+// Inflated script to test budget sizing.
+
+// Used block #1
+// FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+// FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+// FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+// FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+// FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+
+function anUnusedFunction() {
+  // Unused block #1
+  console.log('FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES');
+
+  // Unused block #2
+  // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+  // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+  // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+  // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+  // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+  // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+  // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+  // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+  // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+  // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+}
+
+function aUsedFunction () {
+  function aNestedUnusedFunction () {
+    // Unused block #3
+    console.log('FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES');
+    // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+    // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+    // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+    // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+    // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+  }
+
+  if (false) {
+    // Unused block #4
+    console.log('FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES');
+    // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+    // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+    // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+    // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+    // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+  }
+
+  if (true) {
+    // Unused block #2
+    console.log('FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES');
+    // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+    // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+    // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+    // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+    // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+  }
+
+  // Unused block #3
+  console.log('FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES');
+  // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+  // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+  // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+  // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+  // FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES -- FILLER DATA JUST TO OCCUPY BYTES
+}
diff --git a/lighthouse-cli/test/fixtures/perf/perf-budgets/load-things.html b/lighthouse-cli/test/fixtures/perf/perf-budgets/load-things.html
new file mode 100644
index 000000000..a0cca3060
--- /dev/null
+++ b/lighthouse-cli/test/fixtures/perf/perf-budgets/load-things.html
@@ -0,0 +1,46 @@
+<!doctype html>
+<!--
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
+-->
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
+    <style>
+      @font-face {
+        font-family: 'Lobster';
+        src: local('Lobster'), url('../lobster-v20-latin-regular.woff2') format('woff2');
+      }
+      @font-face {
+        font-family: 'Lobster Two';
+        src: local("Lobster Two"), url("../lobster-two-v10-latin-700.woff2") format('woff2');
+      }
+      .webfont {
+        font-family: Lobster, sans-serif;
+      }
+      strong.webfont {
+        font-family: Lobster Two, sans-serif;
+      }
+    </style>
+    <link rel="stylesheet" href="http://localhost:10503/perf/cors-fonts.css">
+    <script src="big-script.js"></script>
+    <script>
+      async function yah() {
+        // Load file as 'other' resource.
+        const response = await fetch('http://localhost:10503/preload.html');
+        const ab = await response.arrayBuffer();
+        document.querySelector('#corsfont').textContent = ab.length;
+      }
+      yah();
+    </script>
+  </head>
+  <body>
+    <img src="../../dobetterweb/lighthouse-480x318.jpg" width="480" height="57">
+    <img style="width: 100px; height: 100px;" src="../../byte-efficiency/large.svg">
+    <p class="webfont">Ripping off some webfont smoke tests</p>
+    <p><strong class="webfont">Do we need such text</strong></p>
+    <p id="corsfont"></p>
+    <script src="../level-2.js"></script>
+  </body>
+</html>
diff --git a/lighthouse-cli/test/smokehouse/dobetterweb/dbw-expectations.js b/lighthouse-cli/test/smokehouse/dobetterweb/dbw-expectations.js
index 230403e1b..c39d18480 100644
--- a/lighthouse-cli/test/smokehouse/dobetterweb/dbw-expectations.js
+++ b/lighthouse-cli/test/smokehouse/dobetterweb/dbw-expectations.js
@@ -155,12 +155,12 @@ module.exports = [
         },
         'dom-size': {
           score: 1,
-          numericValue: 31,
+          numericValue: 34,
           details: {
             items: [
-              {statistic: 'Total DOM Elements', value: '31'},
+              {statistic: 'Total DOM Elements', value: '34'},
               {statistic: 'Maximum DOM Depth', value: '3'},
-              {statistic: 'Maximum Child Elements', value: '29'},
+              {statistic: 'Maximum Child Elements', value: '32'},
             ],
           },
         },
diff --git a/lighthouse-cli/test/smokehouse/perf/expectations.js b/lighthouse-cli/test/smokehouse/perf/expectations.js
index 8fbf1586f..b0d64f39f 100644
--- a/lighthouse-cli/test/smokehouse/perf/expectations.js
+++ b/lighthouse-cli/test/smokehouse/perf/expectations.js
@@ -6,7 +6,7 @@
 'use strict';
 
 /**
- * Expected Lighthouse audit values for --preset=perf tests
+ * Expected Lighthouse audit values for perf tests.
  */
 module.exports = [
   {
@@ -65,6 +65,84 @@ module.exports = [
       },
     },
   },
+  {
+    lhr: {
+      requestedUrl: 'http://localhost:10200/perf/perf-budgets/load-things.html',
+      finalUrl: 'http://localhost:10200/perf/perf-budgets/load-things.html',
+      audits: {
+        'resource-summary': {
+          score: null,
+          displayValue: '11 requests • 164 KB',
+          details: {
+            items: [
+              {resourceType: 'total', requestCount: 11, size: '168000±1000'},
+              {resourceType: 'font', requestCount: 2, size: '80000±1000'},
+              {resourceType: 'script', requestCount: 3, size: '55000±1000'},
+              {resourceType: 'image', requestCount: 2, size: '28000±1000'},
+              {resourceType: 'document', requestCount: 1, size: '2100±100'},
+              {resourceType: 'other', requestCount: 2, size: '1250±50'},
+              {resourceType: 'stylesheet', requestCount: 1, size: '450±100'},
+              {resourceType: 'media', requestCount: 0, size: 0},
+              {resourceType: 'third-party', requestCount: 0, size: 0},
+            ],
+          },
+        },
+        'performance-budget': {
+          score: null,
+          details: {
+            // Undefined items are asserting that the property isn't included in the table item.
+            items: [
+              {
+                resourceType: 'total',
+                countOverBudget: '3 requests',
+                sizeOverBudget: '65000±1000',
+              },
+              {
+                resourceType: 'script',
+                countOverBudget: '2 requests',
+                sizeOverBudget: '25000±1000',
+              },
+              {
+                resourceType: 'font',
+                countOverBudget: undefined,
+                sizeOverBudget: '4000±500',
+              },
+              {
+                resourceType: 'document',
+                countOverBudget: '1 request',
+                sizeOverBudget: '1100±50',
+              },
+              {
+                resourceType: 'stylesheet',
+                countOverBudget: undefined,
+                sizeOverBudget: '450±100',
+              },
+              {
+                resourceType: 'image',
+                countOverBudget: '1 request',
+                sizeOverBudget: undefined,
+              },
+              {
+                resourceType: 'media',
+                countOverBudget: undefined,
+                sizeOverBudget: undefined,
+              },
+              {
+                resourceType: 'other',
+                countOverBudget: '1 request',
+                sizeOverBudget: undefined,
+              },
+              {
+                resourceType: 'third-party',
+                countOverBudget: undefined,
+                sizeOverBudget: undefined,
+              },
+            ],
+          },
+        },
+      },
+    },
+  },
   {
     lhr: {
       requestedUrl: 'http://localhost:10200/perf/fonts.html',
diff --git a/lighthouse-cli/test/smokehouse/perf/perf-config.js b/lighthouse-cli/test/smokehouse/perf/perf-config.js
new file mode 100644
index 000000000..6b22341a9
--- /dev/null
+++ b/lighthouse-cli/test/smokehouse/perf/perf-config.js
@@ -0,0 +1,50 @@
+/**
+ * @license Copyright 2019 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
+ */
+'use strict';
+
+/** @type {LH.Config.Json} */
+const perfConfig = {
+  extends: 'lighthouse:default',
+  settings: {
+    throttlingMethod: 'devtools',
+    onlyCategories: ['performance'],
+
+    // A mixture of under, over, and meeting budget to exercise all paths.
+    budgets: [{
+      resourceCounts: [
+        {resourceType: 'total', budget: 8},
+        {resourceType: 'stylesheet', budget: 1}, // meets budget
+        {resourceType: 'image', budget: 1},
+        {resourceType: 'media', budget: 0},
+        {resourceType: 'font', budget: 2}, // meets budget
+        {resourceType: 'script', budget: 1},
+        {resourceType: 'document', budget: 0},
+        {resourceType: 'other', budget: 1},
+        {resourceType: 'third-party', budget: 0},
+      ],
+      resourceSizes: [
+        {resourceType: 'total', budget: 100},
+        {resourceType: 'stylesheet', budget: 0},
+        {resourceType: 'image', budget: 30}, // meets budget
+        {resourceType: 'media', budget: 0},
+        {resourceType: 'font', budget: 75},
+        {resourceType: 'script', budget: 30},
+        {resourceType: 'document', budget: 1},
+        {resourceType: 'other', budget: 2}, // meets budget
+        {resourceType: 'third-party', budget: 0},
+      ],
+      timings: [
+        {metric: 'first-contentful-paint', budget: 2000, tolerance: 100},
+        {metric: 'first-cpu-idle', budget: 2000, tolerance: 100},
+        {metric: 'interactive', budget: 2000, tolerance: 100},
+        {metric: 'first-meaningful-paint', budget: 2000, tolerance: 100},
+        {metric: 'estimated-input-latency', budget: 2000, tolerance: 100},
+      ],
+    }],
+  },
+};
+
+module.exports = perfConfig;
diff --git a/lighthouse-cli/test/smokehouse/pwa-expectations.js b/lighthouse-cli/test/smokehouse/pwa-expectations.js
index b4a91c98c..85616240b 100644
--- a/lighthouse-cli/test/smokehouse/pwa-expectations.js
+++ b/lighthouse-cli/test/smokehouse/pwa-expectations.js
@@ -66,6 +66,9 @@ module.exports = [
         'content-width': {
           score: 1,
         },
+        'apple-touch-icon': {
+          score: 1,
+        },
 
         // "manual" audits. Just verify in the results.
         'pwa-cross-browser': {
@@ -128,6 +131,9 @@ module.exports = [
         'content-width': {
           score: 1,
         },
+        'apple-touch-icon': {
+          score: 1,
+        },
 
         // "manual" audits. Just verify in the results.
         'pwa-cross-browser': {
diff --git a/lighthouse-cli/test/smokehouse/pwa2-expectations.js b/lighthouse-cli/test/smokehouse/pwa2-expectations.js
index 3a56038d3..b8b93d09b 100644
--- a/lighthouse-cli/test/smokehouse/pwa2-expectations.js
+++ b/lighthouse-cli/test/smokehouse/pwa2-expectations.js
@@ -61,6 +61,12 @@ module.exports = [
         'content-width': {
           score: 1,
         },
+        'apple-touch-icon': {
+          score: 1,
+          warnings: [
+            /apple-touch-icon-precomposed/,
+          ],
+        },
 
         // "manual" audits. Just verify in the results.
         'pwa-cross-browser': {
@@ -123,6 +129,9 @@ module.exports = [
         'content-width': {
           score: 1,
         },
+        'apple-touch-icon': {
+          score: 0,
+        },
 
         // "manual" audits. Just verify in the results.
         'pwa-cross-browser': {
diff --git a/lighthouse-cli/test/smokehouse/pwa3-expectations.js b/lighthouse-cli/test/smokehouse/pwa3-expectations.js
index 351a34272..d05f37387 100644
--- a/lighthouse-cli/test/smokehouse/pwa3-expectations.js
+++ b/lighthouse-cli/test/smokehouse/pwa3-expectations.js
@@ -58,6 +58,9 @@ module.exports = [
         'content-width': {
           score: 1,
         },
+        'apple-touch-icon': {
+          score: 1,
+        },
 
         // "manual" audits. Just verify in the results.
         'pwa-cross-browser': {
diff --git a/lighthouse-cli/test/smokehouse/smoke-test-dfns.js b/lighthouse-cli/test/smokehouse/smoke-test-dfns.js
index 261eedd14..c12b14fcd 100644
--- a/lighthouse-cli/test/smokehouse/smoke-test-dfns.js
+++ b/lighthouse-cli/test/smokehouse/smoke-test-dfns.js
@@ -67,7 +67,7 @@ const SMOKE_TEST_DFNS = [{
 }, {
   id: 'perf',
   expectations: 'perf/expectations.js',
-  config: 'lighthouse-core/config/perf-config.js',
+  config: 'perf/perf-config.js',
   batch: 'perf-metric',
 }, {
   id: 'lantern',
diff --git a/lighthouse-cli/test/smokehouse/smokehouse-report.js b/lighthouse-cli/test/smokehouse/smokehouse-report.js
index 33b50b502..656561f32 100644
--- a/lighthouse-cli/test/smokehouse/smokehouse-report.js
+++ b/lighthouse-cli/test/smokehouse/smokehouse-report.js
@@ -10,7 +10,7 @@ const log = require('lighthouse-logger');
 
 const VERBOSE = Boolean(process.env.LH_SMOKE_VERBOSE);
 const NUMBER_REGEXP = /(?:\d|\.)+/.source;
-const OPS_REGEXP = /<=?|>=?|\+\/-/.source;
+const OPS_REGEXP = /<=?|>=?|\+\/-|±/.source;
 // An optional number, optional whitespace, an operator, optional whitespace, a number.
 const NUMERICAL_EXPECTATION_REGEXP =
   new RegExp(`^(${NUMBER_REGEXP})?\\s*(${OPS_REGEXP})\\s*(${NUMBER_REGEXP})$`);
@@ -20,6 +20,7 @@ const NUMERICAL_EXPECTATION_REGEXP =
  *    - Greater than/less than operators, e.g. "<100", ">90"
  *    - Regular expressions
  *    - Strict equality
+ *    - plus or minus a margin of error, e.g. '10+/-5', '100±10'
  *
  * @param {*} actual
  * @param {*} expected
@@ -39,6 +40,7 @@ function matchesExpectation(actual, expected) {
       case '<=':
         return actual <= postfixNumber;
       case '+/-':
+      case '±':
         return Math.abs(actual - prefixNumber) <= postfixNumber;
       default:
         throw new Error(`unexpected operator ${operator}`);
@@ -80,16 +82,13 @@ function findDifference(path, actual, expected) {
   }
 
   // We only care that all expected's own properties are on actual (and not the other way around).
+  // Note an expected `undefined` can match an actual that is either `undefined` or not defined.
   for (const key of Object.keys(expected)) {
     // Bracket numbers, but property names requiring quotes will still be unquoted.
     const keyAccessor = /^\d+$/.test(key) ? `[${key}]` : `.${key}`;
     const keyPath = path + keyAccessor;
     const expectedValue = expected[key];
 
-    if (!(key in actual)) {
-      return {path: keyPath, actual: undefined, expected: expectedValue};
-    }
-
     const actualValue = actual[key];
     const subDifference = findDifference(keyPath, actualValue, expectedValue);
 
diff --git a/lighthouse-core/test/audits/apple-touch-icon-test.js b/lighthouse-core/test/audits/apple-touch-icon-test.js
new file mode 100644
index 000000000..39ce2a1b0
--- /dev/null
+++ b/lighthouse-core/test/audits/apple-touch-icon-test.js
@@ -0,0 +1,98 @@
+/**
+ * @license Copyright 2019 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
+ */
+'use strict';
+
+const AppleTouchIcon = require('../../audits/apple-touch-icon.js');
+
+/* eslint-env jest */
+
+describe('PWA: apple-touch-icon audit', () => {
+  it(`fails when apple-touch-icon is not present`, () => {
+    const artifacts = {
+      LinkElements: [
+        {rel: 'iOS-touch-thing', href: 'https://example.com/else.png'},
+        {rel: 'something else', href: 'https://example.com/else.png'},
+      ],
+    };
+
+    const {score} = AppleTouchIcon.audit(artifacts);
+
+    expect(score).toBe(0);
+  });
+
+  it(`fails when apple-touch-icon does not have an href`, () => {
+    const artifacts = {
+      LinkElements: [{rel: 'apple-touch-icon'}],
+    };
+
+    const {score} = AppleTouchIcon.audit(artifacts);
+
+    expect(score).toBe(0);
+  });
+
+  it(`passes but warns when apple-touch-icon-precomposed only`, () => {
+    const artifacts = {
+      LinkElements: [{rel: 'apple-touch-icon-precomposed', href: 'https://example.com/touch-icon.png'}],
+    };
+
+    const {score, warnings} = AppleTouchIcon.audit(artifacts);
+
+    expect(score).toBe(1);
+    expect(warnings[0]).toBeDisplayString('`apple-touch-icon-precomposed` is ' +
+      'out of date; `apple-touch-icon` is preferred.');
+  });
+
+  it(`passes with no warning when precomposed with normal`, () => {
+    const artifacts = {
+      LinkElements: [
+        {rel: 'apple-touch-icon', href: 'https://example.com/touch-icon.png'},
+        {rel: 'apple-touch-icon-precomposed', href: 'https://example.com/touch-icon.png'},
+      ],
+    };
+
+    const {score, warnings} = AppleTouchIcon.audit(artifacts);
+
+    expect(score).toBe(1);
+    expect(warnings).toEqual([]);
+  });
+
+  it(`passes when apple-touch-icon is on page`, () => {
+    const artifacts = {
+      LinkElements: [{rel: 'apple-touch-icon', href: 'https://example.com/touch-icon.png'}],
+    };
+
+    const {score} = AppleTouchIcon.audit(artifacts);
+
+    expect(score).toBe(1);
+  });
+
+  it(`passes with multiple apple-touch-icons on page`, () => {
+    const artifacts = {
+      LinkElements: [
+        {rel: 'apple-touch-icon', sizes: '152x152', href: 'https://example.com/touch-icon.png'},
+        {rel: 'apple-touch-icon', sizes: '180x180', href: 'https://example.com/touch-icon.png'},
+      ],
+    };
+
+    const {score} = AppleTouchIcon.audit(artifacts);
+
+    expect(score).toBe(1);
+  });
+
+  it(`passes when lots of LinkElements`, () => {
+    const artifacts = {
+      LinkElements: [
+        {rel: 'iOS-touch-thing', href: 'https://example.com/else.png'},
+        {rel: 'apple-touch-icon', href: 'https://example.com/touch-icon.png'},
+        {rel: 'something else', href: 'https://example.com/else.png'},
+      ],
+    };
+
+    const {score} = AppleTouchIcon.audit(artifacts);
+
+    expect(score).toBe(1);
+  });
+});
diff --git a/lighthouse-core/test/report/html/renderer/category-renderer-test.js b/lighthouse-core/test/report/html/renderer/category-renderer-test.js
index 8e691f5a5..b18f27521 100644
--- a/lighthouse-core/test/report/html/renderer/category-renderer-test.js
+++ b/lighthouse-core/test/report/html/renderer/category-renderer-test.js
@@ -366,7 +366,7 @@ describe('CategoryRenderer', () => {
       const manualAudits = elem.querySelectorAll('.lh-clump--manual .lh-audit');
 
       assert.equal(passedAudits.length, 2);
-      assert.equal(failedAudits.length, 8);
+      assert.equal(failedAudits.length, 9);
       assert.equal(warningAudits.length, 2);
       assert.equal(manualAudits.length, 3);
     });
@@ -380,7 +380,7 @@ describe('CategoryRenderer', () => {
       const failedAudits = elem.querySelectorAll('.lh-clump--failed .lh-audit');
 
       assert.equal(passedAudits.length, 0);
-      assert.equal(failedAudits.length, 12);
+      assert.equal(failedAudits.length, 13);
     });
 
     it('expands warning audit group', () => {
diff --git a/lighthouse-core/test/report/html/renderer/performance-category-renderer-test.js b/lighthouse-core/test/report/html/renderer/performance-category-renderer-test.js
index 2d03ab6d8..776763a7c 100644
--- a/lighthouse-core/test/report/html/renderer/performance-category-renderer-test.js
+++ b/lighthouse-core/test/report/html/renderer/performance-category-renderer-test.js
@@ -41,6 +41,7 @@ describe('PerfCategoryRenderer', () => {
     const detailsRenderer = new DetailsRenderer(dom);
     renderer = new PerformanceCategoryRenderer(dom, detailsRenderer);
 
+    // TODO: don't call a LH.ReportResult `sampleResults`, which is typically always LH.Result
     sampleResults = Util.prepareReportResult(sampleResultsOrig);
     category = sampleResults.reportCategories.find(cat => cat.id === 'performance');
   });
@@ -173,4 +174,36 @@ describe('PerfCategoryRenderer', () => {
       assert.ok(Number.isFinite(wastedMs), 'Finite number not returned by wastedMs');
     });
   });
+
+  // This is done all in CSS, but tested here.
+  describe('metric description toggles', () => {
+    let container;
+    let toggle;
+    const metricsSelector = '.lh-audit-group--metrics';
+    const toggleSelector = '.lh-metrics-toggle__input';
+    const magicSelector = '.lh-metrics-toggle__input:checked ~ .lh-columns .lh-metric__description';
+    let getDescriptionsAfterCheckedToggle;
+
+    describe('works if there is a performance category', () => {
+      beforeAll(() => {
+        container = renderer.render(category, sampleResults.categoryGroups);
+        const metricsAuditGroup = container.querySelector(metricsSelector);
+        toggle = metricsAuditGroup.querySelector(toggleSelector);
+        // In the CSS, our magicSelector will flip display from `none` to `block`
+        getDescriptionsAfterCheckedToggle = _ => metricsAuditGroup.querySelectorAll(magicSelector);
+      });
+
+      it('descriptions hidden by default', () => {
+        assert.ok(getDescriptionsAfterCheckedToggle().length === 0);
+      });
+
+      it('can toggle description visibility', () => {
+        assert.ok(getDescriptionsAfterCheckedToggle().length === 0);
+        toggle.click();
+        assert.ok(getDescriptionsAfterCheckedToggle().length > 2);
+        toggle.click();
+        assert.ok(getDescriptionsAfterCheckedToggle().length === 0);
+      });
+    });
+  });
 });
diff --git a/lighthouse-core/test/report/html/renderer/report-renderer-test.js b/lighthouse-core/test/report/html/renderer/report-renderer-test.js
index efa3966da..d6d8e6a98 100644
--- a/lighthouse-core/test/report/html/renderer/report-renderer-test.js
+++ b/lighthouse-core/test/report/html/renderer/report-renderer-test.js
@@ -265,10 +265,8 @@ describe('ReportRenderer', () => {
 
     const container = renderer._dom._document.body;
     const reportElement = renderer.renderReport(sampleResults, container);
-    // TODO(khempenius): Remove "+1" once budgets renderer code is added.
-    // Until budgets renderer code is added, JSON vs. DOM comparison will differ by 1.
     const notApplicableElementCount = reportElement
       .querySelectorAll('.lh-audit--notapplicable').length;
-    assert.strictEqual(notApplicableCount, notApplicableElementCount + 1);
+    assert.strictEqual(notApplicableCount, notApplicableElementCount);
   });
 });
diff --git a/lighthouse-core/test/report/html/renderer/report-ui-features-test.js b/lighthouse-core/test/report/html/renderer/report-ui-features-test.js
index 8f2b50d59..4e83cd0cc 100644
--- a/lighthouse-core/test/report/html/renderer/report-ui-features-test.js
+++ b/lighthouse-core/test/report/html/renderer/report-ui-features-test.js
@@ -108,6 +108,15 @@ describe('ReportUIFeatures', () => {
       assert.equal(dom.findAll('.lh-category', container).length, 5);
     });
 
+    it('should init a report with a single category', () => {
+      const lhr = JSON.parse(JSON.stringify(sampleResults));
+      lhr.categories = {
+        performance: lhr.categories.performance,
+      };
+      const container = render(lhr);
+      assert.equal(dom.findAll('.lh-category', container).length, 1);
+    });
+
     describe('third-party filtering', () => {
       let container;
 
@@ -162,41 +171,4 @@ describe('ReportUIFeatures', () => {
       });
     });
   });
-
-  describe('metric description toggles', () => {
-    let container;
-    let metricsAuditGroup;
-    let toggle;
-    const metricsClass = 'lh-audit-group--metrics';
-    const toggleClass = 'lh-metrics-toggle__input';
-    const showClass = 'lh-audit-group--metrics__show-descriptions';
-
-    describe('works if there is a performance category', () => {
-      beforeAll(() => {
-        container = render(sampleResults);
-        metricsAuditGroup = dom.find(`.${metricsClass}`, container);
-        toggle = dom.find(`.${toggleClass}`, metricsAuditGroup);
-      });
-
-      it('descriptions hidden by default', () => {
-        assert.ok(!metricsAuditGroup.classList.contains(showClass));
-      });
-
-      it('can toggle description visibility', () => {
-        assert.ok(!metricsAuditGroup.classList.contains(showClass));
-        toggle.click();
-        assert.ok(metricsAuditGroup.classList.contains(showClass));
-        toggle.click();
-        assert.ok(!metricsAuditGroup.classList.contains(showClass));
-      });
-    });
-
-    it('report still works if performance category does not run', () => {
-      const lhr = JSON.parse(JSON.stringify(sampleResults));
-      delete lhr.categories.performance;
-      container = render(lhr);
-      assert.ok(!container.querySelector(`.${metricsClass}`));
-      assert.ok(!container.querySelector(`.${toggleClass}`));
-    });
-  });
 });
diff --git a/lighthouse-core/test/report/proto-test.js b/lighthouse-core/test/report/proto-test.js
index 8282ffbf3..2c13b20ef 100644
--- a/lighthouse-core/test/report/proto-test.js
+++ b/lighthouse-core/test/report/proto-test.js
@@ -21,23 +21,10 @@ describe('round trip JSON comparison subsets', () => {
     sampleJson = JSON.parse(preprocessor.processForProto(sample));
   });
 
-  it('has the same audit results sans details', () => {
-    Object.keys(sampleJson.audits).forEach(audit => {
-      delete sampleJson.audits[audit].details;
-    });
-
-    expect(roundTripJson.audits).toMatchObject(sampleJson.audits);
-  });
-
-  it('has the same audit results & details if applicable', () => {
-    Object.keys(sampleJson.audits).forEach(auditId => {
-      expect(roundTripJson.audits[auditId]).toMatchObject(sampleJson.audits[auditId]);
-
-      if ('details' in sampleJson.audits[auditId]) {
-        expect(roundTripJson.audits[auditId].details)
-          .toMatchObject(sampleJson.audits[auditId].details);
-      }
-    });
+  it('has the same audit results and details (if applicable)', () => {
+    for (const auditId of Object.keys(sampleJson.audits)) {
+      expect(roundTripJson.audits[auditId]).toEqual(sampleJson.audits[auditId]);
+    }
   });
 
   it('has the same i18n rendererFormattedStrings', () => {
diff --git a/lighthouse-core/test/results/artifacts/artifacts.json b/lighthouse-core/test/results/artifacts/artifacts.json
index 8509b2cdd..93732cd0f 100644
--- a/lighthouse-core/test/results/artifacts/artifacts.json
+++ b/lighthouse-core/test/results/artifacts/artifacts.json
@@ -2002,7 +2002,40 @@
     "status": 404,
     "content": null
   },
-  "TapTargets": [],
+  "TapTargets": [
+    {
+      "clientRects": [
+        {
+          "width": 200,
+          "height": 18,
+          "left": 8,
+          "top": 1268.8125,
+          "right": 208,
+          "bottom": 1286.8125
+        }
+      ],
+      "snippet": "<button class=\"small-button\">Do something</button>",
+      "path": "3,HTML,1,BODY,17,BUTTON",
+      "selector": "body > button.small-button",
+      "href": ""
+    },
+    {
+      "clientRects": [
+        {
+          "width": 200,
+          "height": 18,
+          "left": 8,
+          "top": 1286.8125,
+          "right": 208,
+          "bottom": 1304.8125
+        }
+      ],
+      "snippet": "<button class=\"small-button\">Do something else</button>",
+      "path": "3,HTML,1,BODY,18,BUTTON",
+      "selector": "body > button.small-button",
+      "href": ""
+    }
+  ],
   "ServiceWorker": {
     "versions": [],
     "registrations": []
diff --git a/lighthouse-core/test/results/sample-config.js b/lighthouse-core/test/results/sample-config.js
new file mode 100644
index 000000000..9f8144dbb
--- /dev/null
+++ b/lighthouse-core/test/results/sample-config.js
@@ -0,0 +1,51 @@
+/**
+ * @license Copyright 2019 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
+ */
+'use strict';
+
+/**
+ * @fileoverview Config used for generating the sample_v2 golden LHR.
+ */
+
+/** @type {LH.Config.Json} */
+const budgetedConfig = {
+  extends: 'lighthouse:default',
+  settings: {
+    throttlingMethod: 'devtools',
+    budgets: [{
+      resourceCounts: [
+        {resourceType: 'total', budget: 10},
+        {resourceType: 'stylesheet', budget: 2},
+        {resourceType: 'image', budget: 2},
+        {resourceType: 'media', budget: 0},
+        {resourceType: 'font', budget: 1},
+        {resourceType: 'script', budget: 2},
+        {resourceType: 'document', budget: 1},
+        {resourceType: 'other', budget: 2},
+        {resourceType: 'third-party', budget: 1},
+      ],
+      resourceSizes: [
+        {resourceType: 'total', budget: 100},
+        {resourceType: 'stylesheet', budget: 5},
+        {resourceType: 'image', budget: 30},
+        {resourceType: 'media', budget: 0},
+        {resourceType: 'font', budget: 20},
+        {resourceType: 'script', budget: 30},
+        {resourceType: 'document', budget: 15},
+        {resourceType: 'other', budget: 5},
+        {resourceType: 'third-party', budget: 25},
+      ],
+      timings: [
+        {metric: 'first-contentful-paint', budget: 3000, tolerance: 100},
+        {metric: 'first-cpu-idle', budget: 2900, tolerance: 100},
+        {metric: 'interactive', budget: 2900, tolerance: 100},
+        {metric: 'first-meaningful-paint', budget: 2000, tolerance: 100},
+        {metric: 'estimated-input-latency', budget: 100, tolerance: 100},
+      ],
+    }],
+  },
+};
+
+module.exports = budgetedConfig;
diff --git a/lighthouse-core/test/results/sample_v2.json b/lighthouse-core/test/results/sample_v2.json
index c045c7cdd..01cae44b9 100644
--- a/lighthouse-core/test/results/sample_v2.json
+++ b/lighthouse-core/test/results/sample_v2.json
@@ -476,6 +476,14 @@
         ]
       }
     },
+    "apple-touch-icon": {
+      "id": "apple-touch-icon",
+      "title": "Does not provide a valid `apple-touch-icon`",
+      "description": "For ideal appearance on iOS when users add to the home screen, define an apple-touch-icon. It must point to a non-transparent 192px (or 180px) square PNG. [Learn More](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/).",
+      "score": 0,
+      "scoreDisplayMode": "binary",
+      "warnings": []
+    },
     "splash-screen": {
       "id": "splash-screen",
       "title": "Is not configured for a custom splash screen",
@@ -1276,14 +1284,110 @@
     "performance-budget": {
       "id": "performance-budget",
       "title": "Performance budget",
-      "description": "Keep the quantity and size of network requests under the targets set by the provided performance budget.",
+      "description": "Keep the quantity and size of network requests under the targets set by the provided performance budget. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).",
       "score": null,
-      "scoreDisplayMode": "notApplicable"
+      "scoreDisplayMode": "informative",
+      "details": {
+        "type": "table",
+        "headings": [
+          {
+            "key": "label",
+            "itemType": "text",
+            "text": "Resource Type"
+          },
+          {
+            "key": "requestCount",
+            "itemType": "numeric",
+            "text": "Requests"
+          },
+          {
+            "key": "size",
+            "itemType": "bytes",
+            "text": "Transfer Size"
+          },
+          {
+            "key": "countOverBudget",
+            "itemType": "text",
+            "text": ""
+          },
+          {
+            "key": "sizeOverBudget",
+            "itemType": "bytes",
+            "text": "Over Budget"
+          }
+        ],
+        "items": [
+          {
+            "resourceType": "script",
+            "label": "Script",
+            "requestCount": 4,
+            "size": 103675,
+            "countOverBudget": "2 requests",
+            "sizeOverBudget": 72955
+          },
+          {
+            "resourceType": "total",
+            "label": "Total",
+            "requestCount": 18,
+            "size": 160738,
+            "countOverBudget": "8 requests",
+            "sizeOverBudget": 58338
+          },
+          {
+            "resourceType": "other",
+            "label": "Other",
+            "requestCount": 2,
+            "size": 12861,
+            "sizeOverBudget": 7741
+          },
+          {
+            "resourceType": "third-party",
+            "label": "Third-party",
+            "requestCount": 2,
+            "size": 30174,
+            "countOverBudget": "1 request",
+            "sizeOverBudget": 4574
+          },
+          {
+            "resourceType": "stylesheet",
+            "label": "Stylesheet",
+            "requestCount": 7,
+            "size": 5352,
+            "countOverBudget": "5 requests",
+            "sizeOverBudget": 232
+          },
+          {
+            "resourceType": "image",
+            "label": "Image",
+            "requestCount": 2,
+            "size": 24741
+          },
+          {
+            "resourceType": "media",
+            "label": "Media",
+            "requestCount": 0,
+            "size": 0
+          },
+          {
+            "resourceType": "font",
+            "label": "Font",
+            "requestCount": 0,
+            "size": 0
+          },
+          {
+            "resourceType": "document",
+            "label": "Document",
+            "requestCount": 3,
+            "size": 14109,
+            "countOverBudget": "2 requests"
+          }
+        ]
+      }
     },
     "resource-summary": {
       "id": "resource-summary",
       "title": "Keep request counts low and transfer sizes small",
-      "description": "To set budgets for the quantity and size of page resources, add a budget.json file.",
+      "description": "To set budgets for the quantity and size of page resources, add a budget.json file. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).",
       "score": null,
       "scoreDisplayMode": "informative",
       "displayValue": "18 requests • 157 KB",
@@ -2979,15 +3083,52 @@
     },
     "tap-targets": {
       "id": "tap-targets",
-      "title": "Tap targets are sized appropriately",
+      "title": "Tap targets are not sized appropriately",
       "description": "Interactive elements like buttons and links should be large enough (48x48px), and have enough space around them, to be easy enough to tap without overlapping onto other elements. [Learn more](https://developers.google.com/web/fundamentals/accessibility/accessible-styles#multi-device_responsive_design).",
-      "score": 1,
+      "score": 0,
       "scoreDisplayMode": "binary",
-      "displayValue": "100% appropriately sized tap targets",
+      "displayValue": "0% appropriately sized tap targets",
       "details": {
         "type": "table",
-        "headings": [],
-        "items": []
+        "headings": [
+          {
+            "key": "tapTarget",
+            "itemType": "node",
+            "text": "Tap Target"
+          },
+          {
+            "key": "size",
+            "itemType": "text",
+            "text": "Size"
+          },
+          {
+            "key": "overlappingTarget",
+            "itemType": "node",
+            "text": "Overlapping Target"
+          }
+        ],
+        "items": [
+          {
+            "tapTarget": {
+              "type": "node",
+              "snippet": "<button class=\"small-button\">Do something</button>",
+              "path": "3,HTML,1,BODY,17,BUTTON",
+              "selector": "body > button.small-button"
+            },
+            "overlappingTarget": {
+              "type": "node",
+              "snippet": "<button class=\"small-button\">Do something else</button>",
+              "path": "3,HTML,1,BODY,18,BUTTON",
+              "selector": "body > button.small-button"
+            },
+            "tapTargetScore": 864,
+            "overlappingTargetScore": 720,
+            "overlapScoreRatio": 0.8333333333333334,
+            "size": "200x18",
+            "width": 200,
+            "height": 18
+          }
+        ]
       }
     },
     "hreflang": {
@@ -3049,7 +3190,113 @@
     "disableStorageReset": false,
     "emulatedFormFactor": "mobile",
     "channel": "cli",
-    "budgets": null,
+    "budgets": [
+      {
+        "resourceSizes": [
+          {
+            "resourceType": "total",
+            "budget": 100
+          },
+          {
+            "resourceType": "stylesheet",
+            "budget": 5
+          },
+          {
+            "resourceType": "image",
+            "budget": 30
+          },
+          {
+            "resourceType": "media",
+            "budget": 0
+          },
+          {
+            "resourceType": "font",
+            "budget": 20
+          },
+          {
+            "resourceType": "script",
+            "budget": 30
+          },
+          {
+            "resourceType": "document",
+            "budget": 15
+          },
+          {
+            "resourceType": "other",
+            "budget": 5
+          },
+          {
+            "resourceType": "third-party",
+            "budget": 25
+          }
+        ],
+        "resourceCounts": [
+          {
+            "resourceType": "total",
+            "budget": 10
+          },
+          {
+            "resourceType": "stylesheet",
+            "budget": 2
+          },
+          {
+            "resourceType": "image",
+            "budget": 2
+          },
+          {
+            "resourceType": "media",
+            "budget": 0
+          },
+          {
+            "resourceType": "font",
+            "budget": 1
+          },
+          {
+            "resourceType": "script",
+            "budget": 2
+          },
+          {
+            "resourceType": "document",
+            "budget": 1
+          },
+          {
+            "resourceType": "other",
+            "budget": 2
+          },
+          {
+            "resourceType": "third-party",
+            "budget": 1
+          }
+        ],
+        "timings": [
+          {
+            "metric": "first-contentful-paint",
+            "budget": 3000,
+            "tolerance": 100
+          },
+          {
+            "metric": "first-cpu-idle",
+            "budget": 2900,
+            "tolerance": 100
+          },
+          {
+            "metric": "interactive",
+            "budget": 2900,
+            "tolerance": 100
+          },
+          {
+            "metric": "first-meaningful-paint",
+            "budget": 2000,
+            "tolerance": 100
+          },
+          {
+            "metric": "estimated-input-latency",
+            "budget": 100,
+            "tolerance": 100
+          }
+        ]
+      }
+    ],
     "locale": "en-US",
     "blockedUrlPatterns": null,
     "additionalTraceCategories": null,
@@ -3623,7 +3870,7 @@
         }
       ],
       "id": "seo",
-      "score": 0.82
+      "score": 0.73
     },
     "pwa": {
       "title": "Progressive Web App",
@@ -3690,6 +3937,11 @@
           "weight": 1,
           "group": "pwa-optimized"
         },
+        {
+          "id": "apple-touch-icon",
+          "weight": 1,
+          "group": "pwa-optimized"
+        },
         {
           "id": "pwa-cross-browser",
           "weight": 0
@@ -3704,7 +3956,7 @@
         }
       ],
       "id": "pwa",
-      "score": 0.42
+      "score": 0.41
     }
   },
   "categoryGroups": {
@@ -4073,6 +4325,12 @@
         "duration": 100,
         "entryType": "measure"
       },
+      {
+        "startTime": 0,
+        "name": "lh:audit:apple-touch-icon",
+        "duration": 100,
+        "entryType": "measure"
+      },
       {
         "startTime": 0,
         "name": "lh:audit:splash-screen",
@@ -4911,6 +5169,12 @@
       "lighthouse-core/audits/redirects.js | description": [
         "audits.redirects.description"
       ],
+      "lighthouse-core/audits/apple-touch-icon.js | failureTitle": [
+        "audits[apple-touch-icon].title"
+      ],
+      "lighthouse-core/audits/apple-touch-icon.js | description": [
+        "audits[apple-touch-icon].description"
+      ],
       "lighthouse-core/audits/mainthread-work-breakdown.js | title": [
         "audits[mainthread-work-breakdown].title"
       ],
@@ -4987,47 +5251,88 @@
       "lighthouse-core/audits/performance-budget.js | description": [
         "audits[performance-budget].description"
       ],
-      "lighthouse-core/audits/resource-summary.js | title": [
-        "audits[resource-summary].title"
-      ],
-      "lighthouse-core/audits/resource-summary.js | description": [
-        "audits[resource-summary].description"
+      "lighthouse-core/lib/i18n/i18n.js | scriptResourceType": [
+        "audits[performance-budget].details.items[0].label",
+        "audits[resource-summary].details.items[1].label"
       ],
-      "lighthouse-core/audits/resource-summary.js | displayValue": [
+      "lighthouse-core/audits/performance-budget.js | requestCountOverBudget": [
         {
           "values": {
-            "requestCount": 18,
-            "byteCount": 160738
+            "count": 2
           },
-          "path": "audits[resource-summary].displayValue"
+          "path": "audits[performance-budget].details.items[0].countOverBudget"
+        },
+        {
+          "values": {
+            "count": 8
+          },
+          "path": "audits[performance-budget].details.items[1].countOverBudget"
+        },
+        {
+          "values": {
+            "count": 1
+          },
+          "path": "audits[performance-budget].details.items[3].countOverBudget"
+        },
+        {
+          "values": {
+            "count": 5
+          },
+          "path": "audits[performance-budget].details.items[4].countOverBudget"
+        },
+        {
+          "values": {
+            "count": 2
+          },
+          "path": "audits[performance-budget].details.items[8].countOverBudget"
         }
       ],
       "lighthouse-core/lib/i18n/i18n.js | totalResourceType": [
+        "audits[performance-budget].details.items[1].label",
         "audits[resource-summary].details.items[0].label"
       ],
-      "lighthouse-core/lib/i18n/i18n.js | scriptResourceType": [
-        "audits[resource-summary].details.items[1].label"
-      ],
-      "lighthouse-core/lib/i18n/i18n.js | imageResourceType": [
-        "audits[resource-summary].details.items[2].label"
-      ],
-      "lighthouse-core/lib/i18n/i18n.js | documentResourceType": [
-        "audits[resource-summary].details.items[3].label"
-      ],
       "lighthouse-core/lib/i18n/i18n.js | otherResourceType": [
+        "audits[performance-budget].details.items[2].label",
         "audits[resource-summary].details.items[4].label"
       ],
+      "lighthouse-core/lib/i18n/i18n.js | thirdPartyResourceType": [
+        "audits[performance-budget].details.items[3].label",
+        "audits[resource-summary].details.items[8].label"
+      ],
       "lighthouse-core/lib/i18n/i18n.js | stylesheetResourceType": [
+        "audits[performance-budget].details.items[4].label",
         "audits[resource-summary].details.items[5].label"
       ],
+      "lighthouse-core/lib/i18n/i18n.js | imageResourceType": [
+        "audits[performance-budget].details.items[5].label",
+        "audits[resource-summary].details.items[2].label"
+      ],
       "lighthouse-core/lib/i18n/i18n.js | mediaResourceType": [
+        "audits[performance-budget].details.items[6].label",
         "audits[resource-summary].details.items[6].label"
       ],
       "lighthouse-core/lib/i18n/i18n.js | fontResourceType": [
+        "audits[performance-budget].details.items[7].label",
         "audits[resource-summary].details.items[7].label"
       ],
-      "lighthouse-core/lib/i18n/i18n.js | thirdPartyResourceType": [
-        "audits[resource-summary].details.items[8].label"
+      "lighthouse-core/lib/i18n/i18n.js | documentResourceType": [
+        "audits[performance-budget].details.items[8].label",
+        "audits[resource-summary].details.items[3].label"
+      ],
+      "lighthouse-core/audits/resource-summary.js | title": [
+        "audits[resource-summary].title"
+      ],
+      "lighthouse-core/audits/resource-summary.js | description": [
+        "audits[resource-summary].description"
+      ],
+      "lighthouse-core/audits/resource-summary.js | displayValue": [
+        {
+          "values": {
+            "requestCount": 18,
+            "byteCount": 160738
+          },
+          "path": "audits[resource-summary].displayValue"
+        }
       ],
       "lighthouse-core/audits/accessibility/accesskeys.js | title": [
         "audits.accesskeys.title"
@@ -5270,7 +5575,8 @@
         "audits[render-blocking-resources].details.headings[1].label",
         "audits[unminified-javascript].details.headings[1].label",
         "audits[uses-webp-images].details.headings[2].label",
-        "audits[uses-text-compression].details.headings[1].label"
+        "audits[uses-text-compression].details.headings[1].label",
+        "audits[tap-targets].details.headings[1].text"
       ],
       "lighthouse-core/audits/byte-efficiency/total-byte-weight.js | title": [
         "audits[total-byte-weight].title"
@@ -5456,7 +5762,7 @@
       "lighthouse-core/audits/seo/robots-txt.js | description": [
         "audits[robots-txt].description"
       ],
-      "lighthouse-core/audits/seo/tap-targets.js | title": [
+      "lighthouse-core/audits/seo/tap-targets.js | failureTitle": [
         "audits[tap-targets].title"
       ],
       "lighthouse-core/audits/seo/tap-targets.js | description": [
@@ -5465,11 +5771,17 @@
       "lighthouse-core/audits/seo/tap-targets.js | displayValue": [
         {
           "values": {
-            "decimalProportion": 1
+            "decimalProportion": 0
           },
           "path": "audits[tap-targets].displayValue"
         }
       ],
+      "lighthouse-core/audits/seo/tap-targets.js | tapTargetHeader": [
+        "audits[tap-targets].details.headings[0].text"
+      ],
+      "lighthouse-core/audits/seo/tap-targets.js | overlappingTargetHeader": [
+        "audits[tap-targets].details.headings[2].text"
+      ],
       "lighthouse-core/audits/seo/hreflang.js | title": [
         "audits.hreflang.title"
       ],