Ascendia UI Template - Let's templates
Ascendia preview 1
Free
0 1

Ascendia

By Jonah Hale

Ascendia is a bold salmon-and-teal blog template for an online magazine about women founders, with a vertical side navigation, an offset photo hero, a topic ticker, filterable stories, a day-rate calculator, columnists, events with RSVP and stories, events, about, contact and article pages.

Live Demo
ascendia-tailwind.html
<%# letstemplates: access=premium category=blog niches=blog,consulting,online-courses %>
<!DOCTYPE html>
<html lang="en">
<head>
  <script>
    window.FontAwesomeConfig = { autoReplaceSvg: 'nest' };
  </script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Ascendia | Tailwind CSS Blog Template</title>
  <meta name="description" content="Ascendia is a bold salmon-and-teal Tailwind CSS blog template for an online magazine about women founders, with a vertical side navigation, an offset photo hero, a topic ticker, filterable stories, a day-rate calculator, columnists, events with RSVP and stories, events, about, contact and article pages.">
  <meta name="keywords" content="Ascendia, Tailwind CSS template, HTML website template, blog template, online magazine, women founders, entrepreneurship blog, editorial, events, newsletter">
  <meta name="author" content="Let's templates">
  <link rel="canonical" href="">

  <meta property="og:type" content="product">
  <meta property="og:site_name" content="Let's templates">
  <meta property="og:locale" content="en_US">
  <meta property="og:url" content="">
  <meta property="og:title" content="Ascendia | Tailwind CSS Blog Template">
  <meta property="og:description" content="A bold editorial Tailwind CSS magazine template with a side navigation, filterable stories, a day-rate calculator and article pages.">
  <meta property="og:image" content="">
  <meta property="og:image:alt" content="Ascendia template preview">

  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:url" content="">
  <meta name="twitter:title" content="Ascendia | Tailwind CSS Blog Template">
  <meta name="twitter:description" content="A bold editorial Tailwind CSS magazine template with a side navigation, filterable stories, a day-rate calculator and article pages.">
  <meta name="twitter:image" content="">
  <meta name="twitter:image:alt" content="Ascendia template preview">
  <script src="https://cdn.tailwindcss.com/3.4.17"></script>
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link href="https://fonts.googleapis.com/css2?family=League+Spartan:wght@500;700;800&family=DM+Sans:wght@400;500;700&display=swap" rel="stylesheet">
  <script>
    tailwind.config = {
      theme: {
        extend: {
          colors: { salmon: { DEFAULT: '#f7979a', soft: '#fde3df' }, teal: { DEFAULT: '#06292c', soft: '#123c40' }, cream: '#fff6ef', mustard: '#f2b84b' },
          fontFamily: { display: ['"League Spartan"', 'sans-serif'], sans: ['"DM Sans"', 'sans-serif'] }
        }
      }
    }
  </script>
  <style>
    @view-transition { navigation: auto; }
    ::view-transition-old(root) { animation: 180ms ease-out both page-fade-out; }
    ::view-transition-new(root) { animation: 220ms ease-in both page-fade-in; }
    @keyframes page-fade-out { to { opacity: 0; } }
    @keyframes page-fade-in { from { opacity: 0; } }
    @keyframes ticker { to { transform: translateX(-50%); } }
    html { scroll-behavior: smooth; }
    [hidden] { display: none !important; }
    .ticker { animation: ticker 40s linear infinite; }
    .ticker:hover { animation-play-state: paused; }
    .reveal-ready [data-reveal] { opacity: 0; transform: translateY(24px); transition: opacity .6s ease, transform .6s ease; transition-delay: var(--reveal-delay, 0ms); }
    .reveal-ready [data-reveal].is-visible { opacity: 1; transform: none; }
    @media (prefers-reduced-motion: reduce) {
      html { scroll-behavior: auto; }
      .ticker { animation: none; }
      ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
    }
  </style>
</head>
<body class="bg-cream font-sans text-teal antialiased lg:pl-56">
  <% active_page = "index" %>
  <% nav = { "index" => "Home", "stories" => "Stories", "events" => "Events", "about" => "About", "contact" => "Contact" } %>
  <!-- block:start navbar -->
  <header class="sticky top-0 z-40 bg-salmon lg:fixed lg:inset-y-0 lg:left-0 lg:w-56">
    <nav class="flex h-full items-center justify-between gap-4 px-5 py-4 lg:flex-col lg:items-start lg:justify-start lg:px-8 lg:py-10" aria-label="Main">
      <a href="index.html" class="font-display text-3xl font-extrabold uppercase leading-none tracking-tight text-white">Ascend<span class="text-teal">/</span>ia</a>
      <ul class="hidden flex-col gap-3 font-display text-lg uppercase tracking-wide lg:mt-auto lg:flex">
        <% nav.each do |slug, label| %>
          <% active = active_page == slug %>
          <li><a href="<%= slug %>.html" <%= 'aria-current="page"'.html_safe if active %> class="transition <%= active ? 'font-extrabold text-teal' : 'font-medium text-white hover:text-teal' %>"><%= label %></a></li>
        <% end %>
      </ul>
      <a href="index.html#newsletter" class="hidden w-full bg-teal px-4 py-3 text-center font-display text-sm font-bold uppercase tracking-wider text-white transition hover:bg-white hover:text-teal lg:mt-10 lg:block">Subscribe</a>
      <button type="button" data-menu-toggle aria-controls="mobile-menu" aria-expanded="false" class="grid h-11 w-11 place-items-center border-2 border-teal text-teal lg:hidden" aria-label="Open menu"><i class="fa-solid fa-bars" aria-hidden="true"></i></button>
    </nav>
    <div id="mobile-menu" class="hidden border-t-2 border-teal/20 bg-salmon px-5 pb-6 lg:hidden">
      <ul class="grid gap-1 pt-3 font-display text-3xl uppercase">
        <% nav.each do |slug, label| %>
          <% active = active_page == slug %>
          <li><a href="<%= slug %>.html" <%= 'aria-current="page"'.html_safe if active %> class="block py-2 <%= active ? 'font-extrabold text-teal' : 'font-medium text-white' %>"><%= label %></a></li>
        <% end %>
      </ul>
    </div>
  </header>
  <!-- block:end navbar -->

  <!-- block:start hero -->
  <section class="bg-salmon">
    <div class="mx-auto grid max-w-6xl gap-12 px-5 pb-20 pt-10 md:px-10 lg:grid-cols-[1.2fr_1fr] lg:items-center lg:pt-20">
      <div>
        <p class="font-display text-sm font-bold uppercase tracking-[.2em] text-white" data-reveal>Issue 14 · Spring</p>
        <h1 class="mt-5 font-display text-6xl font-extrabold uppercase leading-[.9] tracking-tight md:text-8xl" data-reveal style="--reveal-delay: 80ms">Look up.<br>Build up.</h1>
        <p class="mt-8 max-w-lg text-lg leading-relaxed" data-reveal style="--reveal-delay: 160ms">Honest stories, playbooks and numbers from women building companies on their own terms. Fewer slogans, more spreadsheets.</p>
        <div class="mt-8 flex flex-wrap gap-3" data-reveal style="--reveal-delay: 220ms">
          <a href="stories.html" class="bg-teal px-7 py-4 font-display text-sm font-bold uppercase tracking-wider text-white transition hover:bg-white hover:text-teal">Read the issue</a>
          <a href="#newsletter" class="border-2 border-teal px-7 py-4 font-display text-sm font-bold uppercase tracking-wider transition hover:bg-teal hover:text-white">Get it weekly</a>
        </div>
      </div>
      <div class="relative mx-auto w-full max-w-md pr-6 pt-6">
        <div class="absolute right-0 top-0 h-[85%] w-[85%] bg-teal" aria-hidden="true"></div>
        <img src="https://images.unsplash.com/photo-1589156280159-27698a70f29e?auto=format&fit=crop&w=800&h=1000&q=80" alt="Woman looking up at the city skyline at dusk" class="relative aspect-[4/5] w-full object-cover">
        <p class="absolute -bottom-5 left-4 bg-mustard px-4 py-2 font-display text-sm font-bold uppercase tracking-wider">Cover: Imani Cole, founder</p>
      </div>
    </div>
  </section>
  <!-- block:end hero -->

  <% topics = ["Money", "Leadership", "First hires", "Pricing", "Fundraising", "Burnout", "Side projects", "Negotiation"] %>
  <!-- block:start marquee -->
  <section class="overflow-hidden border-y-2 border-teal bg-cream py-5" aria-label="Topics">
    <ul class="ticker flex w-max gap-10 whitespace-nowrap font-display text-3xl font-extrabold uppercase md:text-4xl">
      <% 2.times do |copy| %>
        <% topics.each do |topic| %>
          <li class="flex items-center gap-10" <%= 'aria-hidden="true"'.html_safe if copy == 1 %>><a href="stories.html" class="transition hover:text-salmon" <%= 'tabindex="-1"'.html_safe if copy == 1 %>><%= topic %></a><span class="text-salmon" aria-hidden="true">✳</span></li>
        <% end %>
      <% end %>
    </ul>
  </section>
  <!-- block:end marquee -->

  <% posts = [["money", "Money", "The first hire nobody tells you to make", "Why a part-time bookkeeper beat a marketer for three founders we spoke to.", "first-hire", "1573496359142-b8d87734a5a2", "Smiling woman standing in a modern office", "6 min"], ["pricing", "Pricing", "Charge for the outcome, not the hour", "A four-step script for raising your rates without losing your best clients.", "pricing-yourself", "1551836022-d5d88e9218df", "Two women talking over a laptop at a wooden table", "8 min"], ["leadership", "Leadership", "The quiet launch: shipping without an audience", "How a ceramicist sold out her first collection to forty email subscribers.", "quiet-launch", "1522202176988-66273c2fd55f", "Three women working together at laptops", "5 min"]] %>
  <!-- block:start blog -->
  <section class="mx-auto max-w-6xl px-5 py-20 md:px-10 md:py-28" data-latest>
    <div class="flex flex-wrap items-end justify-between gap-6">
      <h2 class="font-display text-5xl font-extrabold uppercase leading-none md:text-6xl">This week</h2>
      <div class="flex flex-wrap gap-2" role="tablist" aria-label="Story topics">
        <% [["all", "All"], ["money", "Money"], ["pricing", "Pricing"], ["leadership", "Leadership"]].each_with_index do |(key, label), index| %>
          <button type="button" role="tab" id="latest-tab-<%= key %>" aria-controls="latest-list" aria-selected="<%= index.zero? %>" tabindex="<%= index.zero? ? 0 : -1 %>" data-topic="<%= key %>" class="border-2 border-teal px-4 py-2 font-display text-sm font-bold uppercase tracking-wider transition hover:bg-salmon-soft aria-selected:bg-teal aria-selected:text-white"><%= label %></button>
        <% end %>
      </div>
    </div>
    <ul id="latest-list" role="tabpanel" aria-label="Latest stories" class="mt-12 grid gap-10 md:grid-cols-3">
      <% posts.each_with_index do |(topic, label, title, text, slug, photo, alt, time), index| %>
        <li data-post-topic="<%= topic %>" data-reveal style="--reveal-delay: <%= index * 80 %>ms">
          <a href="<%= slug %>.html" class="group block">
            <span class="relative block pb-3 pr-3"><span class="absolute bottom-0 right-0 h-[90%] w-[90%] bg-salmon transition group-hover:translate-x-1 group-hover:translate-y-1" aria-hidden="true"></span><img src="https://images.unsplash.com/photo-<%= photo %>?auto=format&fit=crop&w=700&h=520&q=75" alt="<%= alt %>" class="relative aspect-[4/3] w-full object-cover"></span>
            <span class="mt-5 flex items-center gap-3 font-display text-xs font-bold uppercase tracking-[.2em]"><span class="bg-teal px-2 py-1 text-white"><%= label %></span><%= time %> read</span>
            <span class="mt-3 block font-display text-2xl font-bold uppercase leading-tight group-hover:underline group-hover:decoration-salmon group-hover:decoration-4 group-hover:underline-offset-4"><%= title %></span>
            <span class="mt-2 block text-teal/75"><%= text %></span>
          </a>
        </li>
      <% end %>
    </ul>
    <p class="mt-8 text-sm text-teal/60" aria-live="polite"><span data-post-count><%= posts.size %></span> stories shown · <a href="stories.html" class="font-bold underline underline-offset-4">See the archive</a></p>
  </section>
  <!-- block:end blog -->

  <!-- block:start demo -->
  <section class="bg-teal text-white" data-rate>
    <div class="mx-auto grid max-w-6xl gap-12 px-5 py-20 md:px-10 md:py-28 lg:grid-cols-[1fr_1.1fr]">
      <div>
        <p class="font-display text-sm font-bold uppercase tracking-[.2em] text-salmon">Tool of the issue</p>
        <h2 class="mt-4 font-display text-5xl font-extrabold uppercase leading-[.95] md:text-6xl">What should your day rate be?</h2>
        <p class="mt-6 max-w-md text-white/75">Most of our readers undercharge by a third. Put in what you want to earn and how you really work; we will do the maths.</p>
        <div class="mt-10 grid gap-7">
          <% [["income", "Take-home goal", 20000, 200000, 2500, 72500, "$"], ["weeks", "Working weeks a year", 30, 50, 1, 44, ""], ["days", "Billable days a week", 1, 5, 0.5, 3, ""], ["costs", "Yearly business costs", 0, 40000, 500, 9000, "$"]].each do |(key, label, min, max, step, value, prefix)| %>
            <label class="grid gap-2"><span class="flex justify-between font-display text-sm font-bold uppercase tracking-wider"><%= label %><output class="text-mustard" data-rate-out="<%= key %>"><%= prefix %><%= value.is_a?(Integer) && value > 999 ? value.to_s.reverse.scan(/\d{1,3}/).join(',').reverse : value %></output></span><input type="range" min="<%= min %>" max="<%= max %>" step="<%= step %>" value="<%= value %>" data-rate-input="<%= key %>" data-prefix="<%= prefix %>" class="accent-[#f7979a]"></label>
          <% end %>
        </div>
      </div>
      <div class="self-center">
        <div class="relative pb-4 pr-4">
          <div class="absolute bottom-0 right-0 h-full w-full translate-x-0 bg-salmon" aria-hidden="true"></div>
          <div class="relative bg-cream p-8 text-teal md:p-10" aria-live="polite">
            <p class="font-display text-sm font-bold uppercase tracking-[.2em]">Your day rate</p>
            <p class="mt-2 font-display text-7xl font-extrabold md:text-8xl" data-rate-day>$801</p>
            <dl class="mt-8 grid grid-cols-2 gap-6 border-t-2 border-teal pt-6">
              <div><dt class="text-sm text-teal/60">Billable days a year</dt><dd class="font-display text-3xl font-bold" data-rate-days>132</dd></div>
              <div><dt class="text-sm text-teal/60">Revenue needed</dt><dd class="font-display text-3xl font-bold" data-rate-revenue>$105,667</dd></div>
            </dl>
            <p class="mt-6 text-sm text-teal/70">Includes 25% set aside for tax. <a href="pricing-yourself.html" class="font-bold underline underline-offset-4">Read: charge for the outcome</a></p>
          </div>
        </div>
      </div>
    </div>
  </section>
  <!-- block:end demo -->

  <% writers = [["Imani Cole", "Money editor", "money", "1494790108377-be9c29b29330", "Smiling woman in a red top"], ["Sasha Brenner", "Leadership", "leadership", "1544005313-94ddf0286df2", "Woman with short hair by a lake"], ["Marisol Vega", "Pricing columnist", "pricing", "1438761681033-6461ffad8d80", "Woman in a striped top in soft light"], ["Noor Haddad", "Money", "money", "1607746882042-944635dfe10e", "Woman with dark hair against a dark wall"], ["Freya Lund", "Leadership", "leadership", "1573497019940-1c28c88b4f3e", "Woman with curly hair smiling in an office"]] %>
  <!-- block:start team -->
  <section class="mx-auto max-w-6xl px-5 py-20 md:px-10 md:py-28" data-writers>
    <div class="flex flex-wrap items-end justify-between gap-6">
      <h2 class="font-display text-5xl font-extrabold uppercase leading-none md:text-6xl">The columnists</h2>
      <div class="flex flex-wrap items-center gap-2">
        <div class="flex gap-2" role="group" aria-label="Filter columnists">
          <% [["all", "All"], ["money", "Money"], ["leadership", "Leadership"], ["pricing", "Pricing"]].each do |(key, label)| %>
            <button type="button" data-beat="<%= key %>" aria-pressed="<%= key == 'all' %>" class="px-3 py-2 font-display text-sm font-bold uppercase tracking-wider underline-offset-8 transition hover:text-salmon aria-pressed:underline aria-pressed:decoration-4 aria-pressed:decoration-salmon"><%= label %></button>
          <% end %>
        </div>
        <button type="button" data-writers-prev class="grid h-11 w-11 place-items-center border-2 border-teal transition hover:bg-teal hover:text-white" aria-label="Previous columnists"><i class="fa-solid fa-arrow-left" aria-hidden="true"></i></button>
        <button type="button" data-writers-next class="grid h-11 w-11 place-items-center bg-teal text-white transition hover:bg-salmon" aria-label="Next columnists"><i class="fa-solid fa-arrow-right" aria-hidden="true"></i></button>
      </div>
    </div>
    <ul class="mt-12 flex snap-x gap-5 overflow-x-auto pb-4" data-writers-track>
      <% writers.each do |(name, beat, key, photo, alt)| %>
        <li class="w-64 shrink-0 snap-start" data-writer-beat="<%= key %>">
          <img src="https://images.unsplash.com/photo-<%= photo %>?auto=format&fit=crop&w=500&h=620&q=75" alt="<%= alt %>" class="aspect-[4/5] w-full object-cover grayscale transition hover:grayscale-0">
          <p class="mt-4 font-display text-xl font-bold uppercase"><%= name %></p>
          <p class="text-sm text-teal/60"><%= beat %></p>
        </li>
      <% end %>
    </ul>
  </section>
  <!-- block:end team -->

  <% events = [["Apr 18", "Pricing clinic", "Online · 18:00 CET", 40, 33], ["May 06", "Founders' breakfast", "Lisbon · 08:30", 24, 21], ["Jun 12", "Raise your first round", "Online · 17:00 CET", 120, 64]] %>
  <!-- block:start events -->
  <section class="bg-salmon-soft" data-rsvp>
    <div class="mx-auto max-w-6xl px-5 py-20 md:px-10 md:py-28">
      <div class="flex flex-wrap items-end justify-between gap-6">
        <h2 class="font-display text-5xl font-extrabold uppercase leading-none md:text-6xl">Meet the readers</h2>
        <a href="events.html" class="font-display text-sm font-bold uppercase tracking-wider underline decoration-salmon decoration-4 underline-offset-8">All events</a>
      </div>
      <ul class="mt-12 border-t-2 border-teal">
        <% events.each do |(date, title, place, seats, taken)| %>
          <li class="grid gap-4 border-b-2 border-teal py-6 md:grid-cols-[8rem_1fr_auto_auto] md:items-center">
            <span class="font-display text-3xl font-extrabold uppercase text-salmon"><%= date %></span>
            <span><span class="block font-display text-2xl font-bold uppercase"><%= title %></span><span class="text-teal/70"><%= place %></span></span>
            <span class="text-sm font-bold" data-seats><%= seats - taken %> seats left</span>
            <button type="button" data-rsvp-button data-left="<%= seats - taken %>" aria-pressed="false" class="w-fit border-2 border-teal px-5 py-2 font-display text-sm font-bold uppercase tracking-wider transition hover:bg-teal hover:text-white aria-pressed:bg-teal aria-pressed:text-white">RSVP</button>
          </li>
        <% end %>
      </ul>
    </div>
  </section>
  <!-- block:end events -->

  <% notes = [["“The only newsletter I read before my coffee is finished.”", "Dana, bakery owner", "md:col-span-2 bg-teal text-white"], ["“Raised my rates 40% after one issue.”", "Leah, designer", "bg-mustard"], ["“Practical, funny, never preachy.”", "Ama, agency founder", "bg-salmon"], ["“Their first-hire piece saved me a bad decision.”", "Yuki, e-commerce", "bg-white"], ["“The events are worth the trip alone.”", "Mei, studio founder", "bg-salmon-soft"], ["“Feels like advice from a friend who has done it.”", "Rosa, consultant", "md:col-span-3 bg-cream border-2 border-teal"]] %>
  <!-- block:start reviews -->
  <section class="mx-auto max-w-6xl px-5 py-20 md:px-10 md:py-28">
    <h2 class="font-display text-5xl font-extrabold uppercase leading-none md:text-6xl">Readers say</h2>
    <ul class="mt-12 grid gap-4 md:grid-cols-3">
      <% notes.each_with_index do |(quote, name, cls), index| %>
        <li class="flex flex-col justify-between gap-6 p-8 <%= cls %>" data-reveal style="--reveal-delay: <%= (index % 3) * 70 %>ms">
          <p class="font-display text-2xl font-bold uppercase leading-tight md:text-3xl"><%= quote %></p>
          <p class="text-sm font-bold uppercase tracking-wider opacity-75"><%= name %></p>
        </li>
      <% end %>
    </ul>
  </section>
  <!-- block:end reviews -->

  <!-- block:start newsletter -->
  <section id="newsletter" class="bg-salmon">
    <div class="mx-auto grid max-w-6xl gap-10 px-5 py-20 md:px-10 lg:grid-cols-[1fr_1.2fr] lg:items-center">
      <h2 class="font-display text-5xl font-extrabold uppercase leading-[.9] md:text-7xl">One email. Sundays. Free.</h2>
      <form data-newsletter novalidate>
        <fieldset>
          <legend class="font-display text-sm font-bold uppercase tracking-wider">I want more on</legend>
          <div class="mt-3 flex flex-wrap gap-2">
            <% ["Money", "Pricing", "Leadership", "Fundraising", "Wellbeing"].each_with_index do |topic, index| %>
              <label class="cursor-pointer"><input type="checkbox" name="topics" value="<%= topic %>" class="peer sr-only" <%= 'checked' if index < 2 %>><span class="block border-2 border-teal px-4 py-2 font-display text-sm font-bold uppercase tracking-wider transition hover:bg-salmon-soft peer-checked:bg-teal peer-checked:text-white peer-focus-visible:ring-2 peer-focus-visible:ring-white"><%= topic %></span></label>
            <% end %>
          </div>
        </fieldset>
        <div class="mt-6 flex flex-col gap-2 sm:flex-row">
          <label for="news-email" class="sr-only">Email address</label>
          <input id="news-email" name="email" type="email" required autocomplete="email" placeholder="you@company.com" class="min-w-0 flex-1 border-2 border-teal bg-cream px-5 py-4 placeholder:text-teal/40 focus:outline-none focus:ring-4 focus:ring-white">
          <button type="submit" class="bg-teal px-7 py-4 font-display text-sm font-bold uppercase tracking-wider text-white transition hover:bg-white hover:text-teal">Subscribe</button>
        </div>
        <p class="mt-3 min-h-[1.5rem] font-medium" data-form-status role="status"></p>
      </form>
    </div>
  </section>
  <!-- block:end newsletter -->

  <!-- block:start footer -->
  <footer class="overflow-hidden bg-teal text-white">
    <div class="mx-auto max-w-6xl px-5 pt-16 md:px-10">
      <div class="flex flex-wrap justify-between gap-8">
        <ul class="flex flex-wrap gap-6 font-display text-sm font-bold uppercase tracking-wider">
          <% nav.drop(1).each do |slug, label| %><li><a href="<%= slug %>.html" class="hover:text-salmon"><%= label %></a></li><% end %>
          <li><a href="first-hire.html" class="hover:text-salmon">Latest story</a></li>
        </ul>
        <div class="flex gap-4">
          <% [["instagram", "Instagram"], ["linkedin-in", "LinkedIn"], ["spotify", "Podcast"]].each do |(icon, label)| %>
            <a href="contact.html" aria-label="<%= label %>" class="grid h-10 w-10 place-items-center border-2 border-white transition hover:border-salmon hover:bg-salmon"><i class="fa-brands fa-<%= icon %>" aria-hidden="true"></i></a>
          <% end %>
        </div>
      </div>
      <p class="mt-12 select-none font-display text-[19vw] font-extrabold uppercase leading-[.8] tracking-tight text-salmon lg:text-[11rem]" aria-hidden="true">Ascendia</p>
      <p class="flex flex-wrap justify-between gap-4 border-t border-white/20 py-6 text-sm text-white/60"><span>&copy; 2026 Ascendia Media</span><span>Independent, reader-funded, ad-free.</span></p>
    </div>
  </footer>
  <!-- block:end footer -->

  <script>
    const reducedMotion = matchMedia('(prefers-reduced-motion: reduce)').matches;
    const watchMotion = !reducedMotion && !navigator.webdriver && 'IntersectionObserver' in window;

    document.querySelectorAll('[data-menu-toggle]').forEach((button) => {
      const menu = document.getElementById(button.getAttribute('aria-controls'));
      button.addEventListener('click', () => {
        const open = button.getAttribute('aria-expanded') === 'true';
        button.setAttribute('aria-expanded', String(!open));
        menu.classList.toggle('hidden', open);
      });
    });

    document.querySelectorAll('[data-latest]').forEach((root) => {
      const tabs = [...root.querySelectorAll('[role="tab"]')];
      const items = [...root.querySelectorAll('[data-post-topic]')];
      const select = (tab, focus) => {
        tabs.forEach((other) => { const on = other === tab; other.setAttribute('aria-selected', String(on)); other.tabIndex = on ? 0 : -1; });
        let shown = 0;
        items.forEach((item) => { item.hidden = tab.dataset.topic !== 'all' && item.dataset.postTopic !== tab.dataset.topic; if (!item.hidden) shown += 1; });
        root.querySelector('[data-post-count]').textContent = shown;
        if (focus) tab.focus();
      };
      tabs.forEach((tab, i) => {
        tab.addEventListener('click', () => select(tab));
        tab.addEventListener('keydown', (event) => {
          if (event.key === 'ArrowRight') select(tabs[(i + 1) % tabs.length], true);
          if (event.key === 'ArrowLeft') select(tabs[(i - 1 + tabs.length) % tabs.length], true);
        });
      });
    });

    document.querySelectorAll('[data-rate]').forEach((root) => {
      const inputs = Object.fromEntries([...root.querySelectorAll('[data-rate-input]')].map((input) => [input.dataset.rateInput, input]));
      const money = (value) => `$${Math.round(value).toLocaleString('en-US')}`;
      const update = () => {
        Object.values(inputs).forEach((input) => {
          const value = Number(input.value);
          root.querySelector(`[data-rate-out="${input.dataset.rateInput}"]`).textContent = input.dataset.prefix ? money(value) : value;
        });
        const days = Number(inputs.weeks.value) * Number(inputs.days.value);
        const revenue = Number(inputs.income.value) / 0.75 + Number(inputs.costs.value);
        root.querySelector('[data-rate-days]').textContent = days;
        root.querySelector('[data-rate-revenue]').textContent = money(revenue);
        root.querySelector('[data-rate-day]').textContent = money(revenue / days);
      };
      Object.values(inputs).forEach((input) => input.addEventListener('input', update));
      update();
    });

    document.querySelectorAll('[data-writers]').forEach((root) => {
      const track = root.querySelector('[data-writers-track]');
      const chips = [...root.querySelectorAll('[data-beat]')];
      const step = () => (track.firstElementChild?.getBoundingClientRect().width || 256) + 20;
      const behavior = () => (reducedMotion ? 'auto' : 'smooth');
      root.querySelector('[data-writers-prev]').addEventListener('click', () => track.scrollBy({ left: -step(), behavior: behavior() }));
      root.querySelector('[data-writers-next]').addEventListener('click', () => track.scrollBy({ left: step(), behavior: behavior() }));
      chips.forEach((chip) => chip.addEventListener('click', () => {
        chips.forEach((other) => other.setAttribute('aria-pressed', String(other === chip)));
        root.querySelectorAll('[data-writer-beat]').forEach((item) => { item.hidden = chip.dataset.beat !== 'all' && item.dataset.writerBeat !== chip.dataset.beat; });
        track.scrollTo({ left: 0 });
      }));
    });

    document.querySelectorAll('[data-rsvp]').forEach((root) => {
      root.querySelectorAll('[data-rsvp-button]').forEach((button) => button.addEventListener('click', () => {
        const going = button.getAttribute('aria-pressed') !== 'true';
        button.setAttribute('aria-pressed', String(going));
        button.textContent = going ? 'Going ✓' : 'RSVP';
        button.closest('li').querySelector('[data-seats]').textContent = `${Number(button.dataset.left) - (going ? 1 : 0)} seats left`;
      }));
    });

    document.querySelectorAll('[data-newsletter]').forEach((form) => {
      const status = form.querySelector('[data-form-status]');
      form.addEventListener('submit', (event) => {
        event.preventDefault();
        const email = form.querySelector('[name="email"]');
        if (!email.value.trim() || !email.checkValidity()) { status.textContent = 'Please enter a valid email address.'; email.focus(); return; }
        const topics = new FormData(form).getAll('topics');
        status.textContent = `You're in. First issue this Sunday${topics.length ? `, with extra ${topics.join(' and ').toLowerCase()}` : ''}.`;
        form.reset();
      });
    });

    if (watchMotion) {
      document.documentElement.classList.add('reveal-ready');
      const observer = new IntersectionObserver((entries) => entries.forEach((entry) => {
        if (entry.isIntersecting) { entry.target.classList.add('is-visible'); observer.unobserve(entry.target); }
      }), { threshold: 0.15 });
      document.querySelectorAll('[data-reveal]').forEach((el) => observer.observe(el));
    }
  </script>
</body>
</html>

Unlimited access to all templates

Unlock our entire library of premium UI kits, dashboards, and landing pages. Renews automatically, cancel anytime.

$20 / 3 months or $60 / year
Get Unlimited Access

More Blog Templates