diff --git a/.formatter.exs b/.formatter.exs
index 18b26c5..7675824 100644
--- a/.formatter.exs
+++ b/.formatter.exs
@@ -1,5 +1,5 @@
 [
-  import_deps: [:ecto, :ecto_sql, :phoenix],
+  import_deps: [:ecto, :ecto_sql, :phoenix, :typed_struct],
   subdirectories: ["priv/*/migrations"],
   plugins: [Styler, Phoenix.LiveView.HTMLFormatter],
   inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}", "priv/*/seeds.exs"]
diff --git a/lib/cms/author.ex b/lib/cms/author.ex
new file mode 100644
index 0000000..65e38a2
--- /dev/null
+++ b/lib/cms/author.ex
@@ -0,0 +1,46 @@
+defmodule CMS.Author do
+  @moduledoc """
+  Properties of the author, Sloane
+
+  Modeled after the [h-card] specification.
+
+  [h-card]: https://microformats.org/wiki/h-card
+  """
+  use TypedStruct
+
+  @public_properties ~w[name nickname url]a
+
+  typedstruct do
+    field :name, String.t()
+    field :given_name, String.t()
+    field :additional_name, String.t()
+    field :family_name, String.t()
+    field :nickname, String.t()
+    field :email, String.t()
+    field :url, String.t()
+  end
+
+  def sloane do
+    %__MODULE__{
+      name: "Sloane Perrault",
+      given_name: "Sloane",
+      additional_name: "Loretta",
+      family_name: "Perrault",
+      nickname: "sloanelybutsurely",
+      email: "sloane@fastmail.com",
+      url: "https://sloanelybutsurely.com"
+    }
+  end
+
+  def full do
+    sloane()
+  end
+
+  def public do
+    author = full()
+
+    for key <- @public_properties, reduce: %__MODULE__{} do
+      acc -> Map.put(acc, key, Map.get(author, key))
+    end
+  end
+end
diff --git a/lib/cms_web/components/core_components.ex b/lib/cms_web/components/core_components.ex
index 5cb0e5b..70f31e3 100644
--- a/lib/cms_web/components/core_components.ex
+++ b/lib/cms_web/components/core_components.ex
@@ -109,6 +109,7 @@ defmodule CMSWeb.CoreComponents do
   attr :format, :string, required: true
   attr :value, :any, default: nil
   attr :formatter, :atom, default: :default
+  attr :timezone, :string, default: "America/New_York"
   attr :global, :global
 
   def timex(%{value: nil} = assigns) do
@@ -117,10 +118,27 @@ defmodule CMSWeb.CoreComponents do
     """
   end
 
-  def timex(assigns) do
+  def timex(%{value: value, timezone: timezone} = assigns) do
+    assigns =
+      assign_new(assigns, :local_value, fn ->
+        case value do
+          %DateTime{} = datetime ->
+            datetime
+
+          %NaiveDateTime{} = naive ->
+            naive
+            |> DateTime.from_naive!("Etc/UTC")
+            |> DateTime.shift_zone!(timezone)
+        end
+      end)
+
     ~H"""
-    <time datetime={Timex.format!(@value, "{ISO:Extended:Z}")} {@global}>
-      {Timex.format!(@value, @format, timex_formatter(@formatter))}
+    <time
+      datetime={Timex.format!(@local_value, "{ISO:Extended}")}
+      title={Timex.format!(@local_value, "{Mshort} {D}, {YYYY}, {h12}:{m} {AM} {Zabbr}")}
+      {@global}
+    >
+      {Timex.format!(@local_value, @format, timex_formatter(@formatter))}
     </time>
     """
   end
diff --git a/lib/cms_web/controllers/page_html/home.html.heex b/lib/cms_web/controllers/page_html/home.html.heex
index 15a2aa8..83270d6 100644
--- a/lib/cms_web/controllers/page_html/home.html.heex
+++ b/lib/cms_web/controllers/page_html/home.html.heex
@@ -13,7 +13,11 @@
               (no title)
             <% end %>
           </span>
-          <.timex value={post.inserted_at} format="{YYYY}-{0M}-{0D}" class="text-gray-500" />
+          <.timex
+            value={post.inserted_at}
+            format="{YYYY}-{0M}-{0D}"
+            class="text-gray-500 text-nowrap"
+          />
         </.link>
       </li>
     </ul>
@@ -33,7 +37,7 @@
             value={status.inserted_at}
             format="{relative}"
             formatter={:relative}
-            class="text-gray-500"
+            class="text-gray-500 text-nowrap"
           />
         </.link>
       </li>
diff --git a/lib/cms_web/controllers/post_html/index.html.heex b/lib/cms_web/controllers/post_html/index.html.heex
index f20baf6..7c6863b 100644
--- a/lib/cms_web/controllers/post_html/index.html.heex
+++ b/lib/cms_web/controllers/post_html/index.html.heex
@@ -9,7 +9,11 @@
           (no title)
         <% end %>
       </span>
-      <.timex value={post.inserted_at} format="{YYYY}-{0M}-{0D}" class="text-gray-500" />
+      <.timex
+        value={post.inserted_at}
+        format="{YYYY}-{0M}-{0D}"
+        class="text-gray-500 text-nowrap"
+      />
     </.link>
   </li>
 </ul>
diff --git a/lib/cms_web/controllers/status_html/index.html.heex b/lib/cms_web/controllers/status_html/index.html.heex
index c3105a9..3e21c2a 100644
--- a/lib/cms_web/controllers/status_html/index.html.heex
+++ b/lib/cms_web/controllers/status_html/index.html.heex
@@ -9,7 +9,7 @@
         value={status.inserted_at}
         format="{relative}"
         formatter={:relative}
-        class="text-gray-500"
+        class="text-gray-500 text-nowrap"
       />
     </.link>
   </li>
diff --git a/mix.exs b/mix.exs
index 4465a93..1a94067 100644
--- a/mix.exs
+++ b/mix.exs
@@ -52,6 +52,7 @@ defmodule CMS.MixProject do
       {:bandit, "~> 1.5"},
       {:argon2_elixir, "~> 4.1"},
       {:timex, "~> 3.7"},
+      {:typed_struct, "~> 0.3.0"},
 
       # dev/test only
       {:styler, "~> 1.4", only: [:dev, :test], runtime: false}
diff --git a/mix.lock b/mix.lock
index 30611a1..c80adeb 100644
--- a/mix.lock
+++ b/mix.lock
@@ -44,6 +44,7 @@
   "telemetry_poller": {:hex, :telemetry_poller, "1.1.0", "58fa7c216257291caaf8d05678c8d01bd45f4bdbc1286838a28c4bb62ef32999", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9eb9d9cbfd81cbd7cdd24682f8711b6e2b691289a0de6826e58452f28c103c8f"},
   "thousand_island": {:hex, :thousand_island, "1.3.10", "a9971ebab1dfb36e2710a86b37c3f54973fbc9470d892035334415521fb53328", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "17ab1f1b13aadb1f4b4c8e5b59c06874d701119fed082884c9c6d38addad254f"},
   "timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"},
+  "typed_struct": {:hex, :typed_struct, "0.3.0", "939789e3c1dca39d7170c87f729127469d1315dcf99fee8e152bb774b17e7ff7", [:mix], [], "hexpm", "c50bd5c3a61fe4e198a8504f939be3d3c85903b382bde4865579bc23111d1b6d"},
   "tzdata": {:hex, :tzdata, "1.1.2", "45e5f1fcf8729525ec27c65e163be5b3d247ab1702581a94674e008413eef50b", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "cec7b286e608371602318c414f344941d5eb0375e14cfdab605cca2fe66cba8b"},
   "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
   "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},