diff --git a/gulpfile.js b/gulpfile.js
index 8c7b7af394123119164838581bd15800f78e0ab3..d62e87b588523cd1e55f9fe1fcecd20cb05ef376 100755
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -33,12 +33,17 @@ const postcssPurgecss = require("@fullhuman/postcss-purgecss")({
   ],
   defaultExtractor: (content) => content.match(/[A-Za-z0-9-_:/]+/g) || [],
   whitelistPatterns: [
-    /^w-\d/,
-    /^h-\d/,
-    /^max-w-*/,
-    /^elevation-\d/,
-    /^text-*/,
-    /^ico--*/,
+    /(sm|md|lg|xl)?(\\:)?w-\d\d?$/, // width
+    /(sm|md|lg|xl)?(\\:)?h-\d\d?$/, // height
+    /(sm|md|lg|xl)?(\\:)m.?-\d\d?$/, // margins
+    /(sm|md|lg|xl)?(\\:)p.?-\d\d?$/, // paddings
+    /max-w-*/, // maximum width
+    /grid-cols-\d\d?/, // grid columns
+    /col-span-\d\d?/, // grid spans
+    /row-span-\d\d?/, // grid spans
+    /(sm|md|lg|xl)(\\:)text-.*/,
+    // /text-*/,
+    /^ico--[a-z]+\:before$/, // icons
   ],
 });
 
diff --git a/source/_patterns/01-molecules/calendar/calendar-table-row.mustache b/source/_patterns/01-molecules/calendar/calendar-table-row.mustache
index 761b689727cc69be564cad69b02e283c8124c5ad..c3071ef7acafd271d2a6e3faeb7be4d254223a5c 100644
--- a/source/_patterns/01-molecules/calendar/calendar-table-row.mustache
+++ b/source/_patterns/01-molecules/calendar/calendar-table-row.mustache
@@ -3,11 +3,11 @@
   <div class="col-span-8 grid grid-cols-3 calendar-table-row__col">
     <div class="col-span-3 md:col-span-1">
       <strong class="block">{{ dateverbose }}</strong>
-      <p class="font-light text-sm mt-sm">Celý den</p>
+      <p class="font-light text-sm mt-1">Celý den</p>
     </div>
     <div class="col-span-3 md:col-span-2 mt-4 md:mt-0">
       <strong class="block">{{ headline.short }}</strong>
-      <p class="font-light text-sm mt-sm">{{ excerpt.short }}</p>
+      <p class="font-light text-sm mt-1">{{ excerpt.short }}</p>
     </div>
   </div>
   <div class="col-span-2 text-center font-light calendar-table-row__col">
diff --git a/source/_patterns/01-molecules/cards/image-card.mustache b/source/_patterns/01-molecules/cards/image-card.mustache
index 48b29349f86b1154fa191e80dda37c398bde6d75..136d7f765c77219705a6a59513377c8e16491f21 100644
--- a/source/_patterns/01-molecules/cards/image-card.mustache
+++ b/source/_patterns/01-molecules/cards/image-card.mustache
@@ -1,6 +1,6 @@
 <div class="card {{ classes }}">
   <a href="#">
-    <img class="w-full" src="{{ img.landscape-16x9.src }}" alt="{{ img.landscape-16x9.alt }}" />
+    <img class="w-full {{ imgClasses }}" src="{{ img.landscape-16x9.src }}" alt="{{ img.landscape-16x9.alt }}" />
   </a>
   <div class="card__body {{ bodyClasses }}">
     <h1 class="card-headline mb-2"><a href="#">{{ title }}{{^ title }}{{ headline.short }}{{/ title }}</a></h1>
diff --git a/source/_patterns/01-molecules/flip-clock/flip-clock.mustache b/source/_patterns/01-molecules/flip-clock/flip-clock.mustache
index 8f4fea9fb4923d5272f1da775dde3370f6a2561f..4d9724dd1ce56c8bb17dc68286788da707006efd 100644
--- a/source/_patterns/01-molecules/flip-clock/flip-clock.mustache
+++ b/source/_patterns/01-molecules/flip-clock/flip-clock.mustache
@@ -1,4 +1,4 @@
-<div class="flip-clock __js-root">
+<div class="__js-root">
   <ui-flip-clock
     clock-classes="{{ clockClasses }}"
     slot-classes="{{ slotClasses }}"
diff --git a/source/_patterns/02-organisms/00-global/subnav.mustache b/source/_patterns/02-organisms/00-global/subnav.mustache
index 52ef2e0f6da1367066ddf1f20e35249c0c1cf7b3..79c45c720c0cb58cf29841af8cba51d2964a01e6 100644
--- a/source/_patterns/02-organisms/00-global/subnav.mustache
+++ b/source/_patterns/02-organisms/00-global/subnav.mustache
@@ -1,61 +1,61 @@
 <div class="__js-root">
   <ui-app inline-template>
-    <ui-view-provider :initial="{regions: false, calendar: false}" v-slot="{ isCurrentView, toggleView }">
-      <nav class="subnav">
-        <div class="container container--wide">
-          <div class="flex">
-            <button
-              @click="toggleView('regions')"
-              class="btn btn--condensed btn--grey-500 btn--hoveractive btn--to-white text-sm mr-2"
-              :class="{'btn--activated': isCurrentView('regions')}"
-            >
-              <div class="btn__body">
-                <span>Pardubický kraj</span>
-                <i class="ico--chevron-down ml-4"></i>
-              </div>
-            </button>
+    <ui-calendar-dummy-provider v-slot="{ events, onShowMore, hasMore }">
+      <ui-view-provider :initial="{regions: false, calendar: false}" v-slot="{ isCurrentView, toggleView }">
+          <nav class="subnav">
+            <div class="container container--wide">
+              <div class="flex">
+                <button
+                  @click="toggleView('regions')"
+                  class="btn btn--condensed btn--grey-500 btn--hoveractive btn--to-white text-sm mr-2"
+                  :class="{'btn--activated': isCurrentView('regions')}"
+                >
+                  <div class="btn__body">
+                    <span>Pardubický kraj</span>
+                    <i class="ico--chevron-down ml-4"></i>
+                  </div>
+                </button>
 
-            <button
-              @click="toggleView('calendar')"
-              class="btn btn--inline-icon btn--condensed btn--hoveractive btn--grey-500 btn--to-orange-300 text-sm"
-              :class="{'btn--activated': isCurrentView('calendar')}"
-            >
-              <div class="btn__body">
-                <i class="btn__inline-icon ico--calendar mr-0 md:mr-2 text-orange-300"></i>
-                <span class="hidden md:block">Krajský kalendář</span>
-              </div>
-            </button>
+                <button
+                  @click="toggleView('calendar')"
+                  class="btn btn--inline-icon btn--condensed btn--hoveractive btn--grey-500 btn--to-orange-300 text-sm"
+                  :class="{'btn--activated': isCurrentView('calendar')}"
+                >
+                  <div class="btn__body">
+                    <i class="btn__inline-icon ico--calendar mr-0 md:mr-2 text-orange-300"></i>
+                    <span class="hidden md:block">Krajský kalendář</span>
+                  </div>
+                </button>
 
-            <a href="#" class="btn text-sm max-w-full hidden lg:block" @click="toggleView('calendar')">
-              <div class="btn__body bg-grey-800 text-grey-200 flex divide-x">
-                <span class="pr-4">{{ event.title }}</span>
-                <span class="pl-4">{{ event.date }}</span>
-              </div>
-            </a>
+                <a href="#" class="btn text-sm max-w-full hidden lg:block" @click="toggleView('calendar')" v-if="events.length > 0">
+                  <div class="btn__body bg-grey-800 text-grey-200 flex divide-x">
+                    <span class="pr-4">&#123;{events[0].title}&#125;</span>
+                    <span class="pl-4">&#123;{events[0].allDay ? 'Celý den' :  events[0].startDateVerbose + ', ' + events[0].startTimeVerbose}&#125;</span>
+                  </div>
+                </a>
 
-            <button class="btn btn--inline-icon btn--condensed btn--hoveractive btn--grey-500 btn--to-brands-facebook text-sm ml-2">
-              <div class="btn__body">
-                <i class="btn__inline-icon ico--facebook mr-0 md:mr-2 text-brands-facebook"></i>
-                <span class="hidden md:block">Pardubický kraj</span>
+                <button class="btn btn--inline-icon btn--condensed btn--hoveractive btn--grey-500 btn--to-brands-facebook text-sm ml-2">
+                  <div class="btn__body">
+                    <i class="btn__inline-icon ico--facebook mr-0 md:mr-2 text-brands-facebook"></i>
+                    <span class="hidden md:block">Pardubický kraj</span>
+                  </div>
+                </button>
+              </div>
+            </div>
+          </nav>
+          <aside class="subnav-aside">
+            <div class="subnav-aside__item" :class="{'subnav-aside__item--visible': isCurrentView('regions')}">
+              <a @click="toggleView('regions')" class="subnav-aside__close"><i class="ico--close"></i></a>
+              <ui-region-map class="container container--default"></ui-region-map>
+            </div>
+            <div class="subnav-aside__item" :class="{'subnav-aside__item--visible': isCurrentView('calendar')}">
+              <a @click="toggleView('calendar')" class="subnav-aside__close"><i class="ico--close"></i></a>
+              <div class="container container--default">
+                  <ui-calendar-renderer :events="events" :on-show-more="onShowMore" :has-more="hasMore" :name="'Krajský kalendář'"></ui-calendar-renderer>
               </div>
-            </button>
-          </div>
-        </div>
-      </nav>
-      <aside class="subnav-aside">
-        <div class="subnav-aside__item" :class="{'subnav-aside__item--visible': isCurrentView('regions')}">
-          <a @click="toggleView('regions')" class="subnav-aside__close"><i class="ico--close"></i></a>
-          <ui-region-map class="container container--default"></ui-region-map>
-        </div>
-        <div class="subnav-aside__item" :class="{'subnav-aside__item--visible': isCurrentView('calendar')}">
-          <a @click="toggleView('calendar')" class="subnav-aside__close"><i class="ico--close"></i></a>
-          <div class="container container--default">
-            <ui-calendar-dummy-provider v-slot="{ events, onShowMore, hasMore }">
-              <ui-calendar-renderer :events="events" :on-show-more="onShowMore" :has-more="hasMore" :name="'Krajský kalendář'"></ui-calendar-renderer>
-            </ui-calendar-dummy-provider>
-          </div>
-        </div>
-      </aside>
-    </ui-view-provider>
+            </div>
+          </aside>
+      </ui-view-provider>
+    </ui-calendar-dummy-provider>
   </ui-app>
 </div>
diff --git a/source/_patterns/03-templates/elections.mustache b/source/_patterns/03-templates/elections.mustache
index a5e6ce139c35cd38bddeba123bd610f6ad9d5cee..d335c82566932f7d2ac95f65f699dbfa30298366 100644
--- a/source/_patterns/03-templates/elections.mustache
+++ b/source/_patterns/03-templates/elections.mustache
@@ -67,14 +67,14 @@
           <h1 class="head-alt-md text-center py-8 lg:pt-24 lg:pb-8">Volební program</h1>
 
           <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
-            {{> molecules-image-card(classes: "card--hoveractive", bodyClasses: "p-4", title: "Otevřenost a zapojení občanů" )}}
-            {{> molecules-image-card(classes: "card--hoveractive", bodyClasses: "p-4", title: "Smart city" )}}
-            {{> molecules-image-card(classes: "card--hoveractive", bodyClasses: "p-4", title: "Řízení a organizační struktura města" )}}
-            {{> molecules-image-card(classes: "card--hoveractive", bodyClasses: "p-4", title: "Rozvoj města a bydlení" )}}
-            {{> molecules-image-card(classes: "card--hoveractive", bodyClasses: "p-4", title: "Doprava" )}}
-            {{> molecules-image-card(classes: "card--hoveractive", bodyClasses: "p-4", title: "Veřejný prostor a zeleň" )}}
-            {{> molecules-image-card(classes: "card--hoveractive", bodyClasses: "p-4", title: "Vzdělávání" )}}
-            {{> molecules-image-card(classes: "card--hoveractive", bodyClasses: "p-4", title: "Kultura a volný čas" )}}
+            {{> molecules-image-card(classes: "card--hoveractive", bodyClasses: "p-4", imgClasses: "h-48 object-fit", title: "Otevřenost a zapojení občanů" )}}
+            {{> molecules-image-card(classes: "card--hoveractive", bodyClasses: "p-4", imgClasses: "h-48 object-fit", title: "Smart city" )}}
+            {{> molecules-image-card(classes: "card--hoveractive", bodyClasses: "p-4", imgClasses: "h-48 object-fit", title: "Řízení a organizační struktura města" )}}
+            {{> molecules-image-card(classes: "card--hoveractive", bodyClasses: "p-4", imgClasses: "h-48 object-fit", title: "Rozvoj města a bydlení" )}}
+            {{> molecules-image-card(classes: "card--hoveractive", bodyClasses: "p-4", imgClasses: "h-48 object-fit", title: "Doprava" )}}
+            {{> molecules-image-card(classes: "card--hoveractive", bodyClasses: "p-4", imgClasses: "h-48 object-fit", title: "Veřejný prostor a zeleň" )}}
+            {{> molecules-image-card(classes: "card--hoveractive", bodyClasses: "p-4", imgClasses: "h-48 object-fit", title: "Vzdělávání" )}}
+            {{> molecules-image-card(classes: "card--hoveractive", bodyClasses: "p-4", imgClasses: "h-48 object-fit", title: "Kultura a volný čas" )}}
           </div>
         </template>
 
diff --git a/source/css/molecules/calendar.pcss b/source/css/molecules/calendar.pcss
index dff5a32e8ca805a5aa0ba2301713f73c8a7f47db..7d24b8f7a4ac51b6520e2212a4067a0e4a389b4b 100644
--- a/source/css/molecules/calendar.pcss
+++ b/source/css/molecules/calendar.pcss
@@ -11,7 +11,7 @@
 }
 
 .calendar-table-row__col {
-  @apply h-full border-r border-grey-125 p-2 flex justify-center items-center;
+  @apply h-full border-r border-grey-125 p-2 flex justify-center items-start;
 
   &:first-child {
     @apply border-l;
diff --git a/source/css/molecules/candidate-card.pcss b/source/css/molecules/candidate-card.pcss
index d6af350cc68a9d5cc3440a4c56781bf6a06828d8..d471722c9a242b4cbb301a09ab2d3241cfc0cc8a 100644
--- a/source/css/molecules/candidate-card.pcss
+++ b/source/css/molecules/candidate-card.pcss
@@ -4,6 +4,8 @@
   grid-template-areas: "avatar bio"
                        "affiliation affiliation"
                        "social social";
+  /* Needs to match .candidate-card__avatar width */
+  grid-template-columns: theme("width.24") 1fr;
 }
 
 .candidate-card__avatar {
@@ -23,10 +25,11 @@
 }
 
 .candidate-card__affiliation {
-  @apply text-sm bg-grey-50 flex border-t border-b border-grey-125;
+  /* No vertical padding, proper spacing guaranteed by leading-10 - we do not expect line breaks here. */
+  @apply text-sm bg-grey-50 flex border-t border-b border-grey-125 leading-10;
 
   > div {
-    @apply py-2 px-4 flex items-center;
+    @apply px-4 flex items-center;
 
     &:not(:first-child) {
       @apply border-l border-grey-125;
@@ -69,6 +72,7 @@
 
 @screen sm {
   .candidate-card {
+    @apply flex flex-col h-full;
     padding-top: 3rem;
   }
 
@@ -78,10 +82,15 @@
                          "bio"
                          "affiliation"
                          "social";
+    grid-template-columns: 1fr;
   }
 
   .candidate-card__bio {
     @apply p-4;
+    /* This adjusts the height to keep other parts (like affiliation) uniformly
+    aligned one to another. Adjustment is necessary in case of longer
+    names/descriptions. */
+    min-height: 10rem;
   }
 
   .candidate-card__avatar {
@@ -95,4 +104,8 @@
     left: -0.5rem;
     line-height: 2.5rem;
   }
+
+  .candidate-card__social {
+    @apply flex-grow;
+  }
 }
diff --git a/source/css/molecules/candidate-table-row.pcss b/source/css/molecules/candidate-table-row.pcss
index 4a43ccda2aa36d9eedc2a55eff6fc59071cf787a..230fb6a8a64110e44115a384bf24b18be99576b6 100644
--- a/source/css/molecules/candidate-table-row.pcss
+++ b/source/css/molecules/candidate-table-row.pcss
@@ -5,6 +5,8 @@
   grid-template-areas: "position avatar name"
                        "position avatar bio"
                        "position avatar affiliation";
+  /* Needs to match avatar width */
+  grid-template-columns: auto theme("width.16") 1fr;
 
   &:after {
     content: '';
@@ -47,8 +49,10 @@
 
 @screen md {
   .candidate-table-row {
-    @apply py-1 grid-cols-candidate-table-row;
+    @apply py-1;
     grid-template-areas: "position avatar name bio affiliation";
+    /* Needs to match avatar width */
+    grid-template-columns: auto theme("width.10") 30% 20% 1fr
   }
 
   .candidate-table-row__avatar {
diff --git a/source/js/components/calendar/DummyProvider.vue b/source/js/components/calendar/DummyProvider.vue
index d01a6aec9f1f9121f1da8861f29e1473e892138b..acb233b68d6ba9a9a4115f8b38f2d637af6ad4ce 100644
--- a/source/js/components/calendar/DummyProvider.vue
+++ b/source/js/components/calendar/DummyProvider.vue
@@ -2,11 +2,12 @@
 const initialEvents = [
   {
     id: 2,
-    start: new Date("2020-07-08T10:00:00.000Z"),
+    start: "2020-07-08T10:00:00.000Z",
+    startTimestamp: new Date("2020-07-08T10:00:00.000Z").getTime(),
     startDateVerbose: "středa 8. července 2020",
     startTimeVerbose: "12:00",
     allDay: false,
-    end: new Date("2020-07-08T11:00:00.000Z"),
+    end: "2020-07-08T11:00:00.000Z",
     title: "Pirátský oběd - Chrudim",
     description:
       "Pravidelné setkání pirátů při středečním obědě. Nejen o politice a s chutí.",
@@ -15,22 +16,24 @@ const initialEvents = [
   },
   {
     id: 15,
-    start:  new Date("2020-07-13T19:00:00.000Z"),
+    start:  "2020-07-13T19:00:00.000Z",
+    startTimestamp: new Date("2020-07-13T19:00:00.000Z").getTime(),
     startDateVerbose: "pondělí 13. července 2020",
     startTimeVerbose: "21:00",
     allDay: false,
-    end: new Date("2020-07-13T19:30:00.000Z"),
+    end: "2020-07-13T19:30:00.000Z",
     title: "Mumble - předsednictvo",
     link:
       "https://www.google.com/calendar/event?eid=YzVpM2FvaGc2MHAzY2I5aGM1aW1jYjlrNjBvbThiYjE2dGk2NGI5ajY4cjY0ZGhrNzVnamdjOWdjb18yMDIwMDcxM1QxOTAwMDBaIDdyNjczcmxoMjU1b2Zvcmh2M29lYjJsMGcwQGc"
   },
   {
     id: 3,
-    start: new Date("2020-07-15T10:00:00.000Z"),
+    start: "2020-07-15T10:00:00.000Z",
+    startTimestamp: new Date("2020-07-15T10:00:00.000Z").getTime(),
     startDateVerbose: "středa 15. července 2020",
     startTimeVerbose: "12:00",
     allDay: false,
-    end: new Date("2020-07-15T11:00:00.000Z"),
+    end: "2020-07-15T11:00:00.000Z",
     title: "Pirátský oběd - Chrudim",
     description:
       "Pravidelné setkání pirátů při středečním obědě. Nejen o politice a s chutí.",
@@ -40,22 +43,24 @@ const initialEvents = [
   },
   {
     id: 16,
-    start: new Date("2020-07-20T19:00:00.000Z"),
+    start: "2020-07-20T19:00:00.000Z",
+    startTimestamp: new Date("2020-07-20T19:00:00.000Z").getTime(),
     startDateVerbose: "pondělí 20. července 2020",
     startTimeVerbose: "21:00",
     allDay: false,
-    end: new Date("2020-07-20T19:30:00.000Z"),
+    end: "2020-07-20T19:30:00.000Z",
     title: "Mumble - předsednictvo",
     link:
       "https://www.google.com/calendar/event?eid=YzVpM2FvaGc2MHAzY2I5aGM1aW1jYjlrNjBvbThiYjE2dGk2NGI5ajY4cjY0ZGhrNzVnamdjOWdjb18yMDIwMDcyMFQxOTAwMDBaIDdyNjczcmxoMjU1b2Zvcmh2M29lYjJsMGcwQGc"
   },
   {
     id: 4,
-    start: new Date("2020-07-22T10:00:00.000Z"),
+    start: "2020-07-22T10:00:00.000Z",
+    startTimestamp: new Date("2020-07-22T10:00:00.000Z").getTime(),
     startDateVerbose: "středa 22. července 2020",
     startTimeVerbose: "12:00",
     allDay: false,
-    end: new Date("2020-07-22T11:00:00.000Z"),
+    end: "2020-07-22T11:00:00.000Z",
     title: "Pirátský oběd - Chrudim",
     description:
       "Pravidelné setkání pirátů při středečním obědě. Nejen o politice a s chutí.",
@@ -64,22 +69,24 @@ const initialEvents = [
   },
   {
     id: 17,
-    start: new Date("2020-07-27T19:00:00.000Z"),
+    start: "2020-07-27T19:00:00.000Z",
+    startTimestamp: new Date("2020-07-27T19:00:00.000Z").getTime(),
     startDateVerbose: "pondělí 27. července 2020",
     startTimeVerbose: "21:00",
     allDay: false,
-    end: new Date("2020-07-27T19:30:00.000Z"),
+    end: "2020-07-27T19:30:00.000Z",
     title: "Mumble - předsednictvo",
     link:
       "https://www.google.com/calendar/event?eid=YzVpM2FvaGc2MHAzY2I5aGM1aW1jYjlrNjBvbThiYjE2dGk2NGI5ajY4cjY0ZGhrNzVnamdjOWdjb18yMDIwMDcyN1QxOTAwMDBaIDdyNjczcmxoMjU1b2Zvcmh2M29lYjJsMGcwQGc"
   },
   {
     id: 5,
-    start: new Date("2020-07-29T10:00:00.000Z"),
+    start: "2020-07-29T10:00:00.000Z",
+    startTimestamp: new Date("2020-07-29T10:00:00.000Z").getTime(),
     startDateVerbose: "středa 29. července 2020",
     startTimeVerbose: "12:00",
     allDay: false,
-    end: new Date("2020-07-29T11:00:00.000Z"),
+    end: "2020-07-29T11:00:00.000Z",
     title: "Pirátský oběd - Chrudim",
     description:
       "Pravidelné setkání pirátů při středečním obědě. Nejen o politice a s chutí.",
@@ -88,22 +95,24 @@ const initialEvents = [
   },
   {
     id: 18,
-    start: new Date("2020-08-03T19:00:00.000Z"),
+    start: "2020-08-03T19:00:00.000Z",
+    startTimestamp: new Date("2020-08-03T19:00:00.000Z").getTime(),
     startDateVerbose: "pondělí 3. srpna 2020",
     startTimeVerbose: "21:00",
     allDay: false,
-    end: new Date("2020-08-03T19:30:00.000Z"),
+    end: "2020-08-03T19:30:00.000Z",
     title: "Mumble - předsednictvo",
     link:
       "https://www.google.com/calendar/event?eid=YzVpM2FvaGc2MHAzY2I5aGM1aW1jYjlrNjBvbThiYjE2dGk2NGI5ajY4cjY0ZGhrNzVnamdjOWdjb18yMDIwMDgwM1QxOTAwMDBaIDdyNjczcmxoMjU1b2Zvcmh2M29lYjJsMGcwQGc"
   },
   {
     id: 6,
-    start: new Date("2020-08-05T10:00:00.000Z"),
+    start: "2020-08-05T10:00:00.000Z",
+    startTimestamp: new Date("2020-08-05T10:00:00.000Z").getTime(),
     startDateVerbose: "středa 5. srpna 2020",
     startTimeVerbose: "12:00",
     allDay: false,
-    end: new Date("2020-08-05T11:00:00.000Z"),
+    end: "2020-08-05T11:00:00.000Z",
     title: "Pirátský oběd - Chrudim",
     description:
       "Pravidelné setkání pirátů při středečním obědě. Nejen o politice a s chutí.",
@@ -115,22 +124,24 @@ const initialEvents = [
 const moreEvents = [
   {
     id: 19,
-    start: new Date("2020-08-10T19:00:00.000Z"),
+    start: "2020-08-10T19:00:00.000Z",
+    startTimestamp: new Date("2020-08-10T19:00:00.000Z").getTime(),
     startDateVerbose: "pondělí 10. srpna 2020",
     startTimeVerbose: "21:00",
     allDay: false,
-    end: new Date("2020-08-10T19:30:00.000Z"),
+    end: "2020-08-10T19:30:00.000Z",
     title: "Mumble - předsednictvo",
     link:
       "https://www.google.com/calendar/event?eid=YzVpM2FvaGc2MHAzY2I5aGM1aW1jYjlrNjBvbThiYjE2dGk2NGI5ajY4cjY0ZGhrNzVnamdjOWdjb18yMDIwMDgxMFQxOTAwMDBaIDdyNjczcmxoMjU1b2Zvcmh2M29lYjJsMGcwQGc"
   },
   {
     id: 7,
-    start: new Date("2020-08-12T10:00:00.000Z"),
+    start: "2020-08-12T10:00:00.000Z",
+    startTimestamp: new Date("2020-08-12T10:00:00.000Z").getTime(),
     startDateVerbose: "středa 12. srpna 2020",
     startTimeVerbose: "12:00",
     allDay: false,
-    end: new Date("2020-08-12T11:00:00.000Z"),
+    end: "2020-08-12T11:00:00.000Z",
     title: "Pirátský oběd - Chrudim",
     description:
       "Pravidelné setkání pirátů při středečním obědě. Nejen o politice a s chutí.",
diff --git a/source/js/components/calendar/GoogleProvider.vue b/source/js/components/calendar/GoogleProvider.vue
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..06e2ff8a8de548ec449cda1b9947727542f862b1 100644
--- a/source/js/components/calendar/GoogleProvider.vue
+++ b/source/js/components/calendar/GoogleProvider.vue
@@ -0,0 +1,105 @@
+<script>
+  const pageSize = 10;
+
+  export default {
+    props: {
+      calendarId: {
+        type: String,
+        required: true,
+      },
+      apiKey: {
+        type: String,
+        required: true,
+      }
+    },
+    data() {
+      return {
+        events: [],
+        toShow: 7,
+      };
+    },
+    computed: {
+      displayedEvents() {
+        return this.events.slice(0, this.toShow);
+      },
+      hasMore() {
+        return this.toShow < this.events.length;
+      },
+    },
+    methods: {
+      onShowMore() {
+        this.toShow += pageSize;
+      },
+      loadEventsFromStorage() {
+        if (window.sessionStorage && window.sessionStorage['__pircal_' + this.calendarId]) {
+          return JSON.parse(window.sessionStorage['__pircal_' + this.calendarId]);
+        }
+      },
+
+      // Store events to sessionStorage if possible to save requests.
+      storeEventsToStorage() {
+        if (window.sessionStorage) {
+          window.sessionStorage['__pircal_' + this.calendarId] = JSON.stringify(this.events);
+        }
+      }
+    },
+    mounted() {
+      const ev = this.loadEventsFromStorage();
+
+      if (! ev) {
+        const toIso = now => now.toISOString().split('.')[0]+"Z";
+        const now = new Date();
+        const timeMin = now.toISOString();
+        const timeMax = new Date(+now + (1000 * 60 * 60 * 24 * 90)).toISOString(); // 90 days ahead
+        const reqUrl = `https://www.googleapis.com/calendar/v3/calendars/${this.calendarId}/events?key=${encodeURIComponent(this.apiKey)}&maxResults=150&timeMin=${encodeURIComponent(timeMin)}&timeMax=${encodeURIComponent(timeMax)}&sanitizeHtml=true&singleEvents=true&maxAtendees=1`;
+
+        let counter = 0;
+
+        fetch(reqUrl)
+          .then(response => {
+            if (!response.ok) {
+              throw new Error("Problem loading events from google");
+            }
+            return response.json()
+          })
+          .then(resp => {
+            this.events = resp.items
+              .map(e => {
+                const start = new Date(e.start.dateTime || e.start.date);
+                const end = new Date(e.end.dateTime || e.end.date);
+
+                const startDateVerbose = start.toLocaleDateString('cs-CZ', {weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'});
+                const startTimeVerbose = start.getHours() + ':' + start.getMinutes().toString().padStart(2, '0');
+
+                const allDay = ! e.start.dateTime;
+
+                return {
+                  id: counter++,
+                  start: start,
+                  startTimestamp: start.getTime(),
+                  startDateVerbose,
+                  startTimeVerbose,
+                  allDay,
+                  end: end,
+                  title: e.summary,
+                  description: e.description,
+                  link: e.htmlLink
+                };
+              })
+              .sort((e1, e2) => e1.start < e2.start ? -1 : 1);
+
+            this.storeEventsToStorage();
+        });
+      } else {
+        this.events = ev;
+      }
+    },
+    render() {
+      return this.$scopedSlots.default({
+        events: this.displayedEvents,
+        hasMore: this.hasMore,
+        onShowMore: this.onShowMore,
+      });
+    }
+  };
+</script>
diff --git a/source/js/components/calendar/Renderer.vue b/source/js/components/calendar/Renderer.vue
index 395bf73c4c6e8374cf1a1085d08c927bd2166823..44197d1b85f0007359f63f2545263bd4d846b76a 100644
--- a/source/js/components/calendar/Renderer.vue
+++ b/source/js/components/calendar/Renderer.vue
@@ -13,16 +13,16 @@
     </div>
     <div class="col-span-4 xl:col-span-3">
       <div class="grid grid-cols-12 items-center calendar-table-row" v-for="event in events" v-bind:key="event.id">
-        <div class="col-span-2 text-orange-300 head-alt-md calendar-table-row__col"><span>{{ event.start | dateDay }}</span></div>
-        <div class="col-span-8 grid grid-cols-3 calendar-table-row__col" :class="{'calendar-table-row__col--norborder': !event.mapLink}">
+        <div class="col-span-2 text-orange-300 head-alt-md calendar-table-row__col"><span>{{ event.startTimestamp | dateDay }}</span></div>
+        <div class="col-span-8 grid grid-cols-3 col-gap-4 calendar-table-row__col" :class="{'calendar-table-row__col--norborder': !event.mapLink}">
           <div class="col-span-3 md:col-span-1">
             <strong class="block">{{ event.startDateVerbose }}</strong>
-            <p class="font-light text-sm mt-sm">{{ event.allDay ? "Celý den" : event.startTimeVerbose }}</p>
+            <p class="font-light text-sm mt-1">{{ event.allDay ? "Celý den" : event.startTimeVerbose }}</p>
           </div>
           <div class="col-span-3 md:col-span-2 mt-4 md:mt-0">
             <a v-if="event.link" v-bind:href="event.link" class="font-bold block" target="_blank" rel="noreferrer noopener">{{ event.title }}</a>
             <strong v-if="!event.link" class="block">{{ event.title }}</strong>
-            <p class="font-light text-sm mt-sm" v-if="event.description">{{ event.description }}</p>
+            <p class="font-light text-sm mt-1" v-if="event.description">{{ event.description }}</p>
           </div>
         </div>
         <div class="col-span-2 text-center font-light calendar-table-row__col">
@@ -58,7 +58,7 @@
     },
     filters: {
       dateDay: (val) => {
-        return `${val.getDate()}.`;
+        return `${new Date(val).getDate()}.`;
       }
     }
   };
diff --git a/source/js/main.js b/source/js/main.js
index 0d4dd8ba286c26a5ba716a3f63661d3dec16b40a..b42e2bfd56e423028b7bd140ad887ccf81f5fc74 100644
--- a/source/js/main.js
+++ b/source/js/main.js
@@ -4,6 +4,7 @@ import { forEachNode } from "./utils";
 
 import Renderer from "./components/calendar/Renderer";
 import DummyProvider from "./components/calendar/DummyProvider";
+import GoogleProvider from "./components/calendar/GoogleProvider";
 import RegionMap from "./components/RegionMap";
 import ViewProvider from "./components/ViewProvider";
 import Navbar from "./components/navbar/Navbar";
@@ -13,6 +14,7 @@ import FlipClock from "./components/FlipClock";
 
 Vue.component("ui-calendar-renderer", Renderer);
 Vue.component("ui-calendar-dummy-provider", DummyProvider);
+Vue.component("ui-calendar-google-provider", GoogleProvider);
 Vue.component("ui-region-map", RegionMap);
 Vue.component("ui-view-provider", ViewProvider);
 Vue.component("ui-navbar", Navbar);
diff --git a/tailwind.config.js b/tailwind.config.js
index fdd036a65f5b9b4d224428a675ff8d943ad7c1ae..dd7c0315d09cf4adab6a64f9fe611d0dffe91279 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -11,9 +11,6 @@ module.exports = {
       maxWidth: {
         'xxs': '16rem',
       },
-      gridTemplateColumns: {
-        'candidate-table-row': 'auto auto auto auto 1fr'
-      },
       opacity: {
         '85': '0.85',
       },
@@ -125,6 +122,11 @@ module.exports = {
   },
   variants: {
     textDecorationColor: ['responsive', 'hover'],
+    background: ['responsive'],
+    appearance: ['responsive'],
+    space: ['responsive'],
+    text: ['responsive', 'hover'],
+    zIndex: ['responsive'],
   },
   plugins: [
     require('tailwind-css-variables')(