diff --git a/Rakefile b/Rakefile
new file mode 100644
index 000000000..921330e6a
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,76 @@
+require "bundler/gem_tasks"
+require "jekyll"
+require "listen"
+
+def listen_ignore_paths(base, options)
+ [
+ /_config\.ya?ml/,
+ /_site/,
+ /\.jekyll-metadata/
+ ]
+end
+
+def listen_handler(base, options)
+ site = Jekyll::Site.new(options)
+ Jekyll::Command.process_site(site)
+ proc do |modified, added, removed|
+ t = Time.now
+ c = modified + added + removed
+ n = c.length
+ relative_paths = c.map{ |p| Pathname.new(p).relative_path_from(base).to_s }
+ print Jekyll.logger.message("Regenerating:", "#{relative_paths.join(", ")} changed... ")
+ begin
+ Jekyll::Command.process_site(site)
+ puts "regenerated in #{Time.now - t} seconds."
+ rescue => e
+ puts "error:"
+ Jekyll.logger.warn "Error:", e.message
+ Jekyll.logger.warn "Error:", "Run jekyll build --trace for more information."
+ end
+ end
+end
+
+task :preview do
+ base = Pathname.new('.').expand_path
+ options = {
+ "source" => base.join('test').to_s,
+ "destination" => base.join('test/_site').to_s,
+ "force_polling" => false,
+ "serving" => true,
+ "theme" => "minimal-mistakes-jekyll"
+ }
+
+ options = Jekyll.configuration(options)
+
+ ENV["LISTEN_GEM_DEBUGGING"] = "1"
+ listener = Listen.to(
+ base.join("_data"),
+ base.join("_includes"),
+ base.join("_layouts"),
+ base.join("_sass"),
+ base.join("assets"),
+ options["source"],
+ :ignore => listen_ignore_paths(base, options),
+ :force_polling => options['force_polling'],
+ &(listen_handler(base, options))
+ )
+
+ begin
+ listener.start
+ Jekyll.logger.info "Auto-regeneration:", "enabled for '#{options["source"]}'"
+
+ unless options['serving']
+ trap("INT") do
+ listener.stop
+ puts " Halting auto-regeneration."
+ exit 0
+ end
+
+ loop { sleep 1000 }
+ end
+ rescue ThreadError
+ # You pressed Ctrl-C, oh my!
+ end
+
+ Jekyll::Commands::Serve.process(options)
+end
diff --git a/_layouts/single-no-ads.html b/_layouts/single-no-ads.html
new file mode 100644
index 000000000..958a8002a
--- /dev/null
+++ b/_layouts/single-no-ads.html
@@ -0,0 +1,85 @@
+---
+layout: default
+---
+
+{% if page.header.overlay_color or page.header.overlay_image or page.header.image %}
+ {% include page__hero.html %}
+{% elsif page.header.video.id and page.header.video.provider %}
+ {% include page__hero_video.html %}
+{% endif %}
+
+{% if page.url != "/" and site.breadcrumbs %}
+ {% unless paginator %}
+ {% include breadcrumbs.html %}
+ {% endunless %}
+{% endif %}
+
+
+ {% include sidebar.html %}
+
+
+ {% if page.title %}{% endif %}
+ {% if page.excerpt %}{% endif %}
+ {% if page.date %}{% endif %}
+ {% if page.last_modified_at %}{% endif %}
+
+
+ {% unless page.header.overlay_color or page.header.overlay_image %}
+
+ {% endunless %}
+
+
+ {{ content }}
+ {% if page.link %}{% endif %}
+
+
+
+
+
+ {% if page.share %}{% include social-share.html %}{% endif %}
+
+ {% include post_pagination.html %}
+
+
+ {% if site.comments.provider and page.comments %}
+ {% include comments.html %}
+ {% endif %}
+
+
+ {% comment %}{% endcomment %}
+ {% if page.id and page.related and site.related_posts.size > 0 %}
+
+
{{ site.data.ui-text[site.locale].related_label | default: "You May Also Enjoy" }}
+
+ {% for post in site.related_posts limit:4 %}
+ {% include archive-single.html type="grid" %}
+ {% endfor %}
+
+
+ {% comment %}{% endcomment %}
+ {% elsif page.id and page.related %}
+
+
{{ site.data.ui-text[site.locale].related_label | default: "You May Also Enjoy" }}
+
+ {% for post in site.posts limit:4 %}
+ {% include archive-single.html type="grid" %}
+ {% endfor %}
+
+
+ {% endif %}
+
diff --git a/banner.js b/banner.js
new file mode 100644
index 000000000..d03c5f444
--- /dev/null
+++ b/banner.js
@@ -0,0 +1,17 @@
+const fs = require('fs');
+const pkg = require('./package.json');
+const filename = 'assets/js/main.min.js';
+const script = fs.readFileSync(filename);
+const padStart = str => ('0' + str).slice(-2)
+const dateObj = new Date;
+const date = `${dateObj.getFullYear()}-${padStart(dateObj.getMonth() + 1)}-${padStart(dateObj.getDate())}`;
+const banner = `/*!
+ * Minimal Mistakes Jekyll Theme ${pkg.version} by ${pkg.author}
+ * Copyright ${dateObj.getFullYear()} Michael Rose - mademistakes.com | @mmistakes
+ * Licensed under ${pkg.license}
+ */
+`;
+
+if (script.slice(0, 3) != '/**') {
+ fs.writeFileSync(filename, banner + script);
+}
\ No newline at end of file
diff --git a/minimal-mistakes-jekyll.gemspec b/minimal-mistakes-jekyll.gemspec
new file mode 100644
index 000000000..8905a25ba
--- /dev/null
+++ b/minimal-mistakes-jekyll.gemspec
@@ -0,0 +1,27 @@
+# coding: utf-8
+
+Gem::Specification.new do |spec|
+ spec.name = "minimal-mistakes-jekyll"
+ spec.version = "4.24.0"
+ spec.authors = ["Michael Rose"]
+
+ spec.summary = %q{A flexible two-column Jekyll theme.}
+ spec.homepage = "https://github.com/mmistakes/minimal-mistakes"
+ spec.license = "MIT"
+
+ spec.metadata["plugin_type"] = "theme"
+
+ spec.files = `git ls-files -z`.split("\x0").select do |f|
+ f.match(%r{^(assets|_(data|includes|layouts|sass)/|(LICENSE|README|CHANGELOG)((\.(txt|md|markdown)|$)))}i)
+ end
+
+ spec.add_runtime_dependency "jekyll", ">= 3.7", "< 5.0"
+ spec.add_runtime_dependency "jekyll-paginate", "~> 1.1"
+ spec.add_runtime_dependency "jekyll-sitemap", "~> 1.3"
+ spec.add_runtime_dependency "jekyll-gist", "~> 1.5"
+ spec.add_runtime_dependency "jekyll-feed", "~> 0.1"
+ spec.add_runtime_dependency "jekyll-include-cache", "~> 0.1"
+
+ spec.add_development_dependency "bundler"
+ spec.add_development_dependency "rake", ">= 12.3.3"
+end
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 000000000..c71c518e0
--- /dev/null
+++ b/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "minimal-mistakes",
+ "private": true,
+ "version": "4.24.0",
+ "description": "Minimal Mistakes 2 column Jekyll theme.",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/mmistakes/minimal-mistakes.git"
+ },
+ "keywords": [
+ "jekyll",
+ "theme",
+ "minimal"
+ ],
+ "author": "Michael Rose",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/mmistakes/minimal-mistakes/issues"
+ },
+ "homepage": "https://mmistakes.github.io/minimal-mistakes/",
+ "engines": {
+ "node": ">= 0.10.0"
+ },
+ "devDependencies": {
+ "npm-run-all": "^4.1.5",
+ "onchange": "^7.1.0",
+ "uglify-js": "^3.13.6"
+ },
+ "scripts": {
+ "uglify": "uglifyjs assets/js/vendor/jquery/jquery-3.5.1.js assets/js/plugins/jquery.fitvids.js assets/js/plugins/jquery.greedy-navigation.js assets/js/plugins/jquery.magnific-popup.js assets/js/plugins/jquery.ba-throttle-debounce.js assets/js/plugins/smooth-scroll.js assets/js/plugins/gumshoe.js assets/js/_main.js -c -m -o assets/js/main.min.js",
+ "add-banner": "node banner.js",
+ "watch:js": "onchange \"assets/js/**/*.js\" -e \"assets/js/main.min.js\" -- npm run build:js",
+ "build:js": "npm run uglify && npm run add-banner"
+ }
+}