diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ab1e78e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,29 @@
+# Binaries
+linuxservice
+*.exe
+*.dll
+*.so
+*.dylib
+
+# Build directories
+/bin/
+/build/
+/dist/
+
+# Logs and caches
+*.log
+*.cache
+
+# OS files
+.DS_Store
+Thumbs.db
+
+# Editor/project files
+.vscode/
+.idea/
+
+# Go
+*.test
+coverage.out
+
+/wiki/
diff --git a/linuxservice b/linuxservice
deleted file mode 100755
index 30c4a42..0000000
Binary files a/linuxservice and /dev/null differ
diff --git a/main.go b/main.go
index 563e47d..bee2213 100644
--- a/main.go
+++ b/main.go
@@ -9,6 +9,7 @@ import (
"log"
"net/http"
"os"
+ "path/filepath"
"strings"
"sync"
"time"
@@ -97,11 +98,35 @@ func NewServer() *Server {
SonBirthDate: getEnv("SON_BIRTH_DATE", "2022-01-01"),
}
+ // Template Funcs
+ funcs := template.FuncMap{
+ "dict": func(values ...interface{}) (map[string]interface{}, error) {
+ if len(values)%2 != 0 {
+ return nil, fmt.Errorf("dict expects even number of args")
+ }
+ m := make(map[string]interface{}, len(values)/2)
+ for i := 0; i < len(values); i += 2 {
+ k, ok := values[i].(string)
+ if !ok {
+ return nil, fmt.Errorf("dict keys must be strings")
+ }
+ m[k] = values[i+1]
+ }
+ return m, nil
+ },
+ }
+
// Parse templates with error handling
- templates, err := template.ParseGlob("templates/*.html")
+ templates, err := template.New("").Funcs(funcs).ParseGlob("templates/*.html")
if err != nil {
log.Fatalf("Failed to parse templates: %v", err)
}
+ // Optionally parse partials if any exist
+ if matches, _ := filepath.Glob("templates/partials/*.html"); len(matches) > 0 {
+ if _, err := templates.ParseFiles(matches...); err != nil {
+ log.Fatalf("Failed to parse partial templates: %v", err)
+ }
+ }
return &Server{
config: config,
@@ -109,6 +134,21 @@ func NewServer() *Server {
}
}
+// cleanupOldSubmissions periodically purges stale rate-limit entries
+func cleanupOldSubmissions() {
+ for {
+ time.Sleep(10 * time.Minute)
+ mu.Lock()
+ cutoff := time.Now().Add(-submissionCooldown)
+ for ip, t := range lastSubmissionTime {
+ if t.Before(cutoff) {
+ delete(lastSubmissionTime, ip)
+ }
+ }
+ mu.Unlock()
+ }
+}
+
// createPageData creates PageData with the given title and current page
func (s *Server) createPageData(title, currentPage string) PageData {
return PageData{
@@ -208,7 +248,7 @@ func (s *Server) handleContactForm(w http.ResponseWriter, r *http.Request, data
mu.Unlock()
// On success, render success message
- data.SuccessMessage = "Bedankt voor uw bericht! We nemen zo snel mogelijk contact met u op."
+ data.SuccessMessage = "Bedankt voor uw bericht! Ik neem zo snel mogelijk contact met u op."
data.FormData = ContactForm{} // Clear form data
s.renderTemplate(w, "contact.html", *data)
}
@@ -238,7 +278,7 @@ func (s *Server) sendToTelegram(form ContactForm) error {
payload := map[string]interface{}{
"chat_id": s.config.TelegramChatID,
"text": message,
- "parse_mode": "Markdown",
+ "parse_mode": "MarkdownV2",
}
jsonData, err := json.Marshal(payload)
@@ -346,23 +386,57 @@ func (s *Server) aboutHandler(w http.ResponseWriter, r *http.Request) {
s.renderTemplate(w, "over-mij.html", data)
}
+// dienstenHandler handles the services page
+func (s *Server) dienstenHandler(w http.ResponseWriter, r *http.Request) {
+ data := s.createPageData("Diensten en tarieven - "+s.config.CompanyName, "diensten")
+ s.renderTemplate(w, "diensten.html", data)
+}
+
+// linuxHandler handles the Linux page (distributions + features)
+func (s *Server) linuxHandler(w http.ResponseWriter, r *http.Request) {
+ data := s.createPageData("Linux distributies en functies - "+s.config.CompanyName, "linux")
+ s.renderTemplate(w, "linux.html", data)
+}
+
// setupRoutes configures all HTTP routes
func (s *Server) setupRoutes() {
// Static files
fs := http.FileServer(http.Dir("static/"))
- http.Handle("/static/", http.StripPrefix("/static/", fs))
+ http.Handle("/static/", http.StripPrefix("/static/", cacheControlMiddleware(fs)))
// Page routes
http.HandleFunc("/", s.homeHandler)
http.HandleFunc("/contact", s.contactHandler)
http.HandleFunc("/over-mij", s.aboutHandler)
+ http.HandleFunc("/diensten", s.dienstenHandler)
+ http.HandleFunc("/linux", s.linuxHandler)
http.HandleFunc("/health", s.healthHandler)
}
func main() {
server := NewServer()
server.setupRoutes()
+ // Start background cleanup for rate limiting map
+ go cleanupOldSubmissions()
log.Printf("Server starting on %s", server.config.Port)
log.Fatal(http.ListenAndServe(server.config.Port, nil))
}
+
+// cacheControlMiddleware sets Cache-Control headers for static assets
+func cacheControlMiddleware(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ path := r.URL.Path
+ switch {
+ case strings.HasSuffix(path, ".css") || strings.HasSuffix(path, ".js") ||
+ strings.HasSuffix(path, ".png") || strings.HasSuffix(path, ".jpg") ||
+ strings.HasSuffix(path, ".jpeg") || strings.HasSuffix(path, ".webp") ||
+ strings.HasSuffix(path, ".svg") || strings.HasSuffix(path, ".ico") ||
+ strings.HasSuffix(path, ".woff2"):
+ w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
+ default:
+ w.Header().Set("Cache-Control", "public, max-age=300")
+ }
+ next.ServeHTTP(w, r)
+ })
+}
diff --git a/static/style.css b/static/css/style.css
similarity index 82%
rename from static/style.css
rename to static/css/style.css
index c458480..56f4e5d 100644
--- a/static/style.css
+++ b/static/css/style.css
@@ -157,7 +157,7 @@ p {
/* Hero Section */
.hero {
- background: linear-gradient(135deg, rgba(236, 253, 245, 0.85) 0%, rgba(209, 250, 229, 0.85) 100%), url('/static/TuxAinrom.webp');
+ background: linear-gradient(135deg, rgba(236, 253, 245, 0.85) 0%, rgba(209, 250, 229, 0.85) 100%), url('/static/images/TuxAinrom.webp');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
@@ -226,6 +226,98 @@ p {
}
}
+/* Universal Card Base Class */
+.card {
+ background: white;
+ border-radius: 12px;
+ padding: 2rem;
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
+ border: 1px solid transparent;
+ display: flex;
+ flex-direction: column;
+}
+
+.card:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 10px 30px rgba(5, 150, 105, 0.15);
+}
+
+.card h3 {
+ color: #047857;
+ margin-bottom: 1rem;
+}
+
+/* Card Variants */
+.card--benefit {
+ background: #f0fdf4;
+ border-color: #bbf7d0;
+ text-align: center;
+}
+
+.card--benefit:hover {
+ border-color: #34d399;
+}
+
+.card--service {
+ background: #f0fdf4;
+ border-left: 4px solid #059669;
+ border-radius: 12px;
+}
+
+.card--service:hover {
+ transform: translateY(-3px);
+}
+
+.card--distro {
+ background: white;
+ border-radius: 16px;
+ min-height: 400px;
+ position: relative;
+ overflow: hidden;
+}
+
+.card--distro::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 4px;
+ background: linear-gradient(90deg, #059669, #10b981);
+}
+
+.card--distro:hover {
+ transform: translateY(-8px);
+ border-color: #a7f3d0;
+}
+
+.card--cta {
+ text-align: center;
+ border: 2px solid #bbf7d0;
+}
+
+/* Card Content Components */
+.card__icon {
+ font-size: 3rem;
+ margin-bottom: 1rem;
+}
+
+.card--benefit .card__icon {
+ font-size: 3rem;
+}
+
+.card--cta .card__icon {
+ font-size: 3rem;
+}
+
+/* Clickable Card Utility */
+.card-link {
+ text-decoration: none;
+ color: inherit;
+ display: block;
+}
+
/* Buttons */
.btn {
padding: 12px 24px;
@@ -269,12 +361,66 @@ p {
font-size: 1.1rem;
}
-/* Benefits Section */
-.benefits {
- padding: 5rem 0;
+/* Universal Section Classes */
+.section {
+ padding: 3.5rem 0;
+}
+
+.section--light {
background: #fff;
}
+.section--light-green {
+ background: #f9fafb;
+}
+
+/* Brand-tinted light background for callouts and CTAs */
+.section--brand {
+ background: #f0fdf4;
+}
+
+.section--hero {
+ padding: 4rem 0;
+ min-height: 80vh;
+}
+
+/* Section Headers */
+.section__title {
+ text-align: center;
+ margin-bottom: 3rem;
+ color: #047857;
+}
+
+.section__subtitle {
+ text-align: center;
+ font-size: 1.1rem;
+ color: #6b7280;
+ margin-bottom: 4rem;
+ max-width: 600px;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+/* Universal Grid Classes */
+.grid {
+ display: grid;
+ gap: 2rem;
+}
+
+.grid--2-col {
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+}
+
+.grid--3-col {
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+}
+
+.grid--responsive-cards {
+ grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
+}
+
+/* Legacy Section Classes - Cleaned Up */
+
.benefits h2 {
text-align: center;
margin-bottom: 3rem;
@@ -287,34 +433,8 @@ p {
gap: 2rem;
}
-.benefit-card {
- background: #f0fdf4;
- padding: 2rem;
- border-radius: 12px;
- text-align: center;
- transition: transform 0.3s ease, box-shadow 0.3s ease;
- border: 1px solid #bbf7d0;
-}
-
-.benefit-card:hover {
- transform: translateY(-5px);
- box-shadow: 0 10px 30px rgba(5, 150, 105, 0.15);
- border-color: #34d399;
-}
-
-.benefit-icon {
- font-size: 3rem;
- margin-bottom: 1rem;
-}
-
-.benefit-card h3 {
- color: #047857;
- margin-bottom: 1rem;
-}
-
/* Windows EOL Section */
.windows-eol {
- padding: 5rem 0;
background: #fffbeb;
position: relative;
overflow: hidden;
@@ -429,10 +549,6 @@ p {
}
/* Distributions Section */
-.distros {
- padding: 5rem 0;
- background: #f9fafb;
-}
.distros h2 {
text-align: center;
@@ -458,36 +574,6 @@ p {
align-items: stretch;
}
-.distro-card {
- background: white;
- border-radius: 16px;
- padding: 2rem;
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
- transition: all 0.3s ease;
- border: 2px solid transparent;
- position: relative;
- overflow: hidden;
- display: flex;
- flex-direction: column;
- min-height: 400px;
-}
-
-.distro-card::before {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- height: 4px;
- background: linear-gradient(90deg, #059669, #10b981);
-}
-
-.distro-card:hover {
- transform: translateY(-8px);
- box-shadow: 0 12px 40px rgba(5, 150, 105, 0.15);
- border-color: #a7f3d0;
-}
-
.distro-header {
display: flex;
align-items: flex-start;
@@ -588,10 +674,6 @@ p {
}
/* Services Section */
-.services {
- padding: 5rem 0;
- background: #fff;
-}
.services h2 {
text-align: center;
@@ -605,28 +687,55 @@ p {
gap: 2rem;
}
-.service-card {
- background: #f0fdf4;
- padding: 2rem;
+/* Steps grid: balanced layout 3/2/1 across breakpoints */
+.steps-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 2rem;
+}
+
+@media (max-width: 1024px) {
+ .steps-grid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+}
+
+@media (max-width: 640px) {
+ .steps-grid {
+ grid-template-columns: 1fr;
+ }
+}
+
+/* Distro tiles */
+.distro-tiles {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
+ gap: 1.5rem;
+}
+
+.distro-tile {
+ background: #fff;
+ border: 1px solid #e5e7eb;
border-radius: 12px;
- border-left: 4px solid #059669;
- transition: transform 0.3s ease;
+ padding: 1.25rem;
+ text-align: center;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.06);
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
}
-.service-card:hover {
- transform: translateY(-3px);
+.distro-tile:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 10px 20px rgba(5,150,105,0.12);
}
-.service-card h3 {
- color: #047857;
- margin-bottom: 1rem;
+.distro-tile__img {
+ width: 72px;
+ height: 72px;
+ object-fit: contain;
+ margin-bottom: 0.75rem;
}
/* Linux Features Section */
-.linux-features {
- padding: 5rem 0;
- background: #f9fafb;
-}
.linux-features h2 {
text-align: center;
@@ -703,6 +812,18 @@ p {
}
+/* Intro GNOME section overrides */
+.intro-gnome .feature-image img {
+ width: 100%;
+ height: auto;
+ object-fit: contain;
+}
+
+.intro-gnome .feature-image {
+ aspect-ratio: auto;
+}
+
+
.feature-showcase h3 {
color: #047857;
@@ -726,7 +847,6 @@ p {
/* CTA Section */
.cta {
- padding: 5rem 0;
background: linear-gradient(135deg, #059669 0%, #047857 100%);
color: white;
text-align: center;
@@ -744,12 +864,43 @@ p {
}
/* Contact Page Styles */
-.contact-hero {
+/* Deprecated: contact-hero replaced by .page-hero */
+
+/* Generic page hero reused across pages */
+.page-hero {
background: linear-gradient(135deg, #ecfdf5 0%, #d1fae5 100%);
- padding: 3rem 0;
+ padding: 2.25rem 0;
text-align: center;
}
+/* Match heading color for reusable hero */
+.page-hero h1 {
+ color: #047857;
+ margin-bottom: 1rem;
+}
+
+/* Keep heading links visually identical to headings */
+h2 a {
+ text-decoration: none;
+ color: inherit;
+}
+
+/* Ensure card titles with links look identical to non-linked titles */
+.card h3 a,
+.card h3 a:visited,
+.card h3 a:hover,
+.card h3 a:active {
+ text-decoration: none;
+ color: inherit;
+}
+
+/* Reusable link wrapper to make whole cards clickable */
+.card-link {
+ text-decoration: none;
+ color: inherit;
+ display: block;
+}
+
.contact-hero h1 {
color: #047857;
margin-bottom: 1rem;
@@ -881,10 +1032,6 @@ p {
min-height: 120px;
}
-.contact-cta {
- padding: 4rem 0;
- background: #f0fdf4;
-}
.contact-cta h2 {
text-align: center;
@@ -898,22 +1045,58 @@ p {
gap: 2rem;
}
+/* Pricing/Tarieven benefit cards */
.cta-benefit {
- text-align: center;
- padding: 2rem;
- background: white;
+ background: #fff;
+ border: 1px solid #e5e7eb;
border-radius: 12px;
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+ padding: 1.5rem;
+ text-align: center;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.06);
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
}
-.cta-benefit .benefit-icon {
- font-size: 3rem;
- margin-bottom: 1rem;
+.cta-benefit:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 10px 20px rgba(5,150,105,0.12);
}
-.cta-benefit h3 {
+.benefit-icon {
+ width: 56px;
+ height: 56px;
+ border-radius: 9999px;
+ background: #ecfdf5;
+ border: 2px solid #bbf7d0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin: 0 auto 0.75rem auto;
+}
+
+.benefit-icon__glyph {
+ font-weight: 700;
color: #047857;
- margin-bottom: 1rem;
+ font-size: 1.25rem;
+ line-height: 1;
+}
+
+/* Collapsible disclaimer styles */
+details.disclaimer {
+ margin-top: 0.5rem;
+}
+
+details.disclaimer summary {
+ cursor: pointer;
+ color: #047857;
+ font-weight: 600;
+}
+
+details.disclaimer .disclaimer-body {
+ background: #f9fafb;
+ border-left: 4px solid #059669;
+ padding: 1rem 1rem 1rem 1.25rem;
+ border-radius: 6px;
+ margin-top: 0.75rem;
}
/* Footer */
@@ -957,8 +1140,8 @@ footer {
}
/* About page */
-.about {
- padding: 5rem 0;
+.section {
+ padding: 3.5rem 0;
}
.about-grid {
@@ -1067,9 +1250,7 @@ footer {
gap: 1.5rem;
}
- .benefit-card {
- padding: 1.5rem;
- }
+
.eol-content {
grid-template-columns: 1fr;
@@ -1081,9 +1262,7 @@ footer {
gap: 1.5rem;
}
- .distro-card {
- padding: 1.5rem;
- }
+
.contact-grid {
grid-template-columns: 1fr;
@@ -1112,8 +1291,11 @@ footer {
}
/* Section padding adjustments */
- .benefits, .windows-eol, .distros, .services, .linux-features, .cta {
- padding: 3rem 0;
+ .page-hero {
+ padding: 1.75rem 0;
+ }
+ .section {
+ padding: 2.25rem 0;
}
/* Typography adjustments */
@@ -1159,12 +1341,12 @@ footer {
}
/* Card padding adjustments */
- .benefit-card, .service-card, .distro-card, .cta-benefit {
+ .card {
padding: 1.25rem;
}
/* Icon size adjustments */
- .benefit-icon, .cta-benefit .benefit-icon {
+ .card__icon {
font-size: 2.5rem;
}
@@ -1196,8 +1378,11 @@ footer {
}
/* Section padding for very small screens */
- .benefits, .windows-eol, .distros, .services, .linux-features, .cta {
- padding: 2rem 0;
+ .page-hero {
+ padding: 1.5rem 0;
+ }
+ .section {
+ padding: 1.75rem 0;
}
/* Typography for very small screens */
@@ -1277,7 +1462,7 @@ footer {
font-size: 1rem;
}
- .benefit-card, .service-card, .distro-card, .cta-benefit {
+ .card {
padding: 1rem;
}
@@ -1299,9 +1484,7 @@ footer {
}
}
-.benefit-card,
-.service-card,
-.cta-benefit {
+.card {
animation: fadeInUp 0.6s ease-out;
}
diff --git a/static/favicon.ico b/static/icons/favicon.ico
similarity index 100%
rename from static/favicon.ico
rename to static/icons/favicon.ico
diff --git a/static/manifest.json b/static/icons/manifest.json
similarity index 80%
rename from static/manifest.json
rename to static/icons/manifest.json
index b515705..7ad6277 100644
--- a/static/manifest.json
+++ b/static/icons/manifest.json
@@ -8,12 +8,12 @@
"theme_color": "#059669",
"icons": [
{
- "src": "/static/android-chrome-192x192.png",
+ "src": "/static/images/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
- "src": "/static/android-chrome-512x512.png",
+ "src": "/static/images/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
diff --git a/static/DutchOpen2024.jpg b/static/images/DutchOpen2024.jpg
similarity index 100%
rename from static/DutchOpen2024.jpg
rename to static/images/DutchOpen2024.jpg
diff --git a/static/GamingBenchmarkLinuxvsWindows.webp b/static/images/GamingBenchmarkLinuxvsWindows.webp
similarity index 100%
rename from static/GamingBenchmarkLinuxvsWindows.webp
rename to static/images/GamingBenchmarkLinuxvsWindows.webp
diff --git a/static/LinuxMintCinnamonSoftwareManager.webp b/static/images/LinuxMintCinnamonSoftwareManager.webp
similarity index 100%
rename from static/LinuxMintCinnamonSoftwareManager.webp
rename to static/images/LinuxMintCinnamonSoftwareManager.webp
diff --git a/static/Tux.svg b/static/images/Tux.svg
similarity index 100%
rename from static/Tux.svg
rename to static/images/Tux.svg
diff --git a/static/TuxAinrom.png b/static/images/TuxAinrom.png
similarity index 100%
rename from static/TuxAinrom.png
rename to static/images/TuxAinrom.png
diff --git a/static/TuxAinrom.webp b/static/images/TuxAinrom.webp
similarity index 100%
rename from static/TuxAinrom.webp
rename to static/images/TuxAinrom.webp
diff --git a/static/Zorin-17-desktop.webp b/static/images/Zorin-17-desktop.webp
similarity index 100%
rename from static/Zorin-17-desktop.webp
rename to static/images/Zorin-17-desktop.webp
diff --git a/static/android-chrome-192x192.png b/static/images/android-chrome-192x192.png
similarity index 100%
rename from static/android-chrome-192x192.png
rename to static/images/android-chrome-192x192.png
diff --git a/static/android-chrome-512x512.png b/static/images/android-chrome-512x512.png
similarity index 100%
rename from static/android-chrome-512x512.png
rename to static/images/android-chrome-512x512.png
diff --git a/static/apple-touch-icon.png b/static/images/apple-touch-icon.png
similarity index 100%
rename from static/apple-touch-icon.png
rename to static/images/apple-touch-icon.png
diff --git a/static/clippy.png b/static/images/clippy.png
similarity index 100%
rename from static/clippy.png
rename to static/images/clippy.png
diff --git a/static/clippy_transparent.png b/static/images/clippy_transparent.png
similarity index 100%
rename from static/clippy_transparent.png
rename to static/images/clippy_transparent.png
diff --git a/static/images/elementary.png b/static/images/elementary.png
new file mode 100644
index 0000000..0c30e68
Binary files /dev/null and b/static/images/elementary.png differ
diff --git a/static/favicon-16x16.png b/static/images/favicon-16x16.png
similarity index 100%
rename from static/favicon-16x16.png
rename to static/images/favicon-16x16.png
diff --git a/static/favicon-32x32.png b/static/images/favicon-32x32.png
similarity index 100%
rename from static/favicon-32x32.png
rename to static/images/favicon-32x32.png
diff --git a/static/images/fedora.png b/static/images/fedora.png
new file mode 100644
index 0000000..3e308b9
Binary files /dev/null and b/static/images/fedora.png differ
diff --git a/static/images/garuda.svg b/static/images/garuda.svg
new file mode 100644
index 0000000..49087c2
--- /dev/null
+++ b/static/images/garuda.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/static/images/gnome.jpg b/static/images/gnome.jpg
new file mode 100644
index 0000000..13820a8
Binary files /dev/null and b/static/images/gnome.jpg differ
diff --git a/static/images/gnome.webp b/static/images/gnome.webp
new file mode 100644
index 0000000..98c49d2
Binary files /dev/null and b/static/images/gnome.webp differ
diff --git a/static/images/mint.svg b/static/images/mint.svg
new file mode 100644
index 0000000..fd38dad
--- /dev/null
+++ b/static/images/mint.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/static/images/popos.svg b/static/images/popos.svg
new file mode 100644
index 0000000..44628a2
--- /dev/null
+++ b/static/images/popos.svg
@@ -0,0 +1 @@
+
diff --git a/static/images/ubuntu.png b/static/images/ubuntu.png
new file mode 100644
index 0000000..69fd1bd
Binary files /dev/null and b/static/images/ubuntu.png differ
diff --git a/templates/contact.html b/templates/contact.html
index 590862b..a944fc2 100644
--- a/templates/contact.html
+++ b/templates/contact.html
@@ -15,7 +15,7 @@
-
+
@@ -26,15 +26,15 @@
-
+
-
-
-
-
-
+
+
+
+
+
@@ -44,25 +44,27 @@
-
+
+
+
-
+
{{template "header" .}}
-