diff --git a/Gitit.hs b/Gitit.hs
--- a/Gitit.hs
+++ b/Gitit.hs
@@ -49,17 +49,22 @@
 import Text.Pandoc.Definition (processPandoc)
 import Text.Pandoc.Shared (HTMLMathMethod(..), substitute)
 import Data.Char (isAlphaNum, isAlpha)
-import Codec.Binary.UTF8.String (decodeString)
 import Control.Monad.Reader
 import qualified Data.ByteString.Lazy as B
 import Network.HTTP (urlEncodeVars, urlEncode)
 import System.Console.GetOpt
 import System.Exit
 import Text.Highlighting.Kate
+import Text.StringTemplate as T
+import Data.IORef
+import System.IO.Unsafe (unsafePerformIO)
 
 gititVersion :: String
-gititVersion = "0.3.1"
+gititVersion = "0.3.2"
 
+template :: IORef (StringTemplate String)
+template = unsafePerformIO $ newIORef $ newSTMP ""  -- initialize template to empty string
+
 main :: IO ()
 main = do
   argv <- getArgs
@@ -68,6 +73,9 @@
   gitPath <- findExecutable "git"
   when (isNothing gitPath) $ error "'git' program not found in system path."
   initializeWiki conf
+  -- initialize template
+  templ <- liftIO $ readFile (templateFile conf)
+  writeIORef template (newSTMP templ)
   control <- startSystemState entryPoint
   update $ SetConfig conf
   -- read user file and update state
@@ -147,6 +155,7 @@
   let repodir = repositoryPath conf
   let frontpage = frontPage conf <.> "page"
   let staticdir = staticDir conf
+  let templatefile = templateFile conf
   repoExists <- doesDirectoryExist repodir
   unless repoExists $ do
     postupdatepath <- getDataFileName $ "data" </> "post-update"
@@ -198,6 +207,11 @@
                        "If you want support for math, copy the jsMath directory into " ++ staticdir ++ "/js/\n" ++
                        "jsMath can be obtained from http://www.math.union.edu/~dpvc/jsMath/\n" ++
                        replicate 80 '*'
+  templateExists <- doesFileExist templatefile
+  unless templateExists $ do
+    templatePath <- getDataFileName $ "data" </> "template.html"
+    copyFile templatePath templatefile
+    hPutStrLn stderr $ "Created " ++ templatefile
 
 type Handler = ServerPart Response
 
@@ -224,6 +238,7 @@
                                          handle isSourceCode GET showFileDiff ]
                , withCommand "export"  [ handlePage POST exportPage, handlePage GET exportPage ]
                , withCommand "cancel"  [ handlePage POST showPage ]
+               , withCommand "discuss" [ handlePage GET discussPage ]
                , withCommand "update"  [ handlePage POST  $ unlessNoEdit $ ifLoggedIn "?edit" updatePage ]
                , withCommand "delete"  [ handlePage GET  $ unlessNoDelete $ ifLoggedIn "?delete" confirmDelete,
                                          handlePage POST $ unlessNoDelete $ ifLoggedIn "?delete" deletePage ]
@@ -331,7 +346,7 @@
 data Command = Command (Maybe String)
 
 commandList :: [String]
-commandList = ["edit", "showraw", "history", "export", "diff", "cancel", "update", "delete"]
+commandList = ["edit", "showraw", "history", "export", "diff", "cancel", "update", "delete", "discuss"]
 
 instance FromData Command where
      fromData = do
@@ -420,6 +435,9 @@
 isPage ('_':_) = False
 isPage s = '.' `notElem` s
 
+isDiscussPage :: String -> Bool
+isDiscussPage s = isPage s && ":discuss" `isSuffixOf` s
+
 isSourceCode :: String -> Bool
 isSourceCode = not . null . languagesByExtension . takeExtension
 
@@ -465,7 +483,7 @@
 randomPage :: String -> Params -> Web Response
 randomPage _ _ = do
   files <- gitLsTree "HEAD" >>= return . map (unwords . drop 3 . words) . lines
-  let pages = map dropExtension $ filter (\f -> takeExtension f == ".page") files
+  let pages = map dropExtension $ filter (\f -> takeExtension f == ".page" && not (":discuss.page" `isSuffixOf` f)) files
   if null pages
      then error "No pages found!"
      else do
@@ -494,6 +512,12 @@
                     then createPage page params
                     else error $ "Invalid revision: " ++ revision
 
+discussPage :: String -> Params -> Web Response
+discussPage page params = do
+  if isDiscussPage page
+     then showPage page params
+     else showPage (page ++ ":discuss") params
+
 createPage :: String -> Params -> Web Response
 createPage page params =
   formattedPage (defaultPageLayout { pgTabs = [] }) page params $
@@ -644,7 +668,7 @@
                            [thespan ! [theclass "date"] << logDate entry, stringToHtml " (",
                             thespan ! [theclass "author"] <<
                                     anchor ! [href $ "/_activity?" ++ urlEncodeVars [("forUser", logAuthor entry)]] <<
-                                               (logAuthor entry), stringToHtml "):",
+                                               (logAuthor entry), stringToHtml "): ",
                             thespan ! [theclass "subject"] << logSubject entry, stringToHtml " (",
                             thespan ! [theclass "files"] << filesFor (logFiles entry),
                             stringToHtml ")"]) hist
@@ -677,7 +701,7 @@
               Nothing -> gitCatFile revision (pathForPage page)
               Just t  -> return $ Just t
   let contents = case raw of
-                      Nothing -> "# Title goes here\n\nContent goes here"
+                      Nothing -> ""
                       Just c  -> c
   sha1 <- case (pSHA1 params) of
                ""  -> gitGetSHA1 (pathForPage page) >>= return . fromMaybe ""
@@ -767,7 +791,7 @@
   let page = "_index"
   let revision = pRevision params
   files <- gitLsTree revision >>= return . map (unwords . drop 3 . words) . lines
-  let htmlIndex = fileListToHtml "/" $ map splitPath $ sort files
+  let htmlIndex = fileListToHtml "/" $ map splitPath $ sort $ filter (\f -> not (":discuss.page" `isSuffixOf` f)) files
   formattedPage (defaultPageLayout { pgShowPageTools = False, pgTabs = [], pgScripts = ["folding.js"], pgTitle = "All pages" }) page params htmlIndex
 
 -- | Map a list of nonempty lists onto a list of pairs of list heads and list of tails.
@@ -781,9 +805,9 @@
 -- | Create a hierarchical ordered list (with links) for a list of files
 fileListToHtml :: String -> [[FilePath]] -> Html
 fileListToHtml prefix lst = ulist ! [identifier "index", theclass "folding"] <<
-  (map (\(h, l) -> let h' = if ".page" `isSuffixOf` h then dropExtension h else h
+  (map (\(h, l) -> let h' = if takeExtension h == ".page" then dropExtension h else h
                    in if [] `elem` l
-                         then li ! [theclass $ if isPage h' then "page" else "upload"] << anchor ! [href $ prefix ++ h'] << h'
+                         then li ! [theclass $ if takeExtension h == ".page" then "page" else "upload"] << anchor ! [href $ prefix ++ h'] << h'
                          else li ! [theclass "folder"] << [stringToHtml h', fileListToHtml (prefix ++ h') l]) $
   consolidateHeads lst)
 
@@ -822,14 +846,14 @@
   , pgSelectedTab    :: Tab
   }
 
-data Tab = ViewTab | EditTab | HistoryTab deriving (Eq, Show)
+data Tab = ViewTab | EditTab | HistoryTab | DiscussTab deriving (Eq, Show)
 
 defaultPageLayout :: PageLayout
 defaultPageLayout = PageLayout
   { pgTitle          = ""
   , pgScripts        = []
   , pgShowPageTools  = True
-  , pgTabs           = [ViewTab, EditTab, HistoryTab]
+  , pgTabs           = [ViewTab, EditTab, HistoryTab, DiscussTab]
   , pgSelectedTab    = ViewTab
   }
 
@@ -842,98 +866,57 @@
              then gitGetSHA1 path' >>= return . fromMaybe ""
              else return revision
   user <- getLoggedInUser params
-  cfg <- (query GetConfig)
-  let stylesheetlinks = 
-        if pPrintable params
-           then thelink ! [href "/css/print.css", rel "stylesheet", strAttr "media" "all", thetype "text/css"] << noHtml
-           else thelink ! [href "/css/screen.css", rel "stylesheet",
-                             strAttr "media" "screen, projection", thetype "text/css"] << noHtml +++
-                primHtml "<!--[if IE]>" +++
-                thelink ! [href "/css/ie.css", rel "stylesheet",
-                          strAttr "media" "screen, projection", thetype "text/css"] << noHtml +++
-                primHtml "<![endif]-->" +++
-                thelink ! [href "/css/hk-pyg.css", rel "stylesheet",
-                           strAttr "media" "screen, projection", thetype "text/css"] << noHtml +++
-                thelink ! [href "/css/print.css", rel "stylesheet", strAttr "media" "print", thetype "text/css"] << noHtml
   let javascriptlinks = if null (pgScripts layout)
-                           then noHtml
-                           else concatHtml $ map
+                           then ""
+                           else renderHtmlFragment $ concatHtml $ map
                                   (\x -> script ! [src ("/js/" ++ x), thetype "text/javascript"] << noHtml)
                                   (["jquery.min.js", "jquery-ui.packed.js"] ++ pgScripts layout)
-  let pageTitle' = pgTitle layout `orIfNull` page
-  let title' = thetitle << (wikiTitle cfg ++ " - " ++ pageTitle')
-  let head' = header << [title', stylesheetlinks, javascriptlinks]
-  let searchbox = gui ("/_search") ! [identifier "searchform"] <<
-                          [ textfield "patterns"
-                          , submit "search" "Search" ]
-  let sitenav = thediv ! [theclass "sitenav"] <<
-                  fieldset <<
-                        [ legend << "Site"
-                        , ulist << (map li
-                             [ anchor ! [href "/"] << "Front page"
-                             , anchor ! [href "/_index"] << "All pages"
-                             , anchor ! [href "/_random"] << "Random page"
-                             , anchor ! [href "/_activity"] << "Recent activity"
-                             , anchor ! [href "/_upload"] << "Upload a file"
-                             , anchor ! [href "/Help"] << "Help" ])
-                        , searchbox ]
-  let tools = if pgShowPageTools layout
-                 then thediv ! [theclass "pageTools"] <<
-                        fieldset <<
-                        [ legend << "This page"
-                        , ulist << (map li
-                             [ anchor ! [href $ urlForPage page ++ "?revision=" ++ revision ++ "&showraw"] << "Raw page source"
-                             , anchor ! [href $ urlForPage page ++ "?revision=" ++ revision ++ "&printable"] << "Printable version"
-                             , anchor ! [href $ urlForPage page ++ "?revision=" ++ sha1] << "Permanent link"
-                             , anchor ! [href $ urlForPage page ++ "?delete"] << "Delete this page" ])
-                        , exportBox page params ]
-                 else noHtml
+  let pageTitle = pgTitle layout `orIfNull` page
   let tabli tab = if tab == pgSelectedTab layout
                      then li ! [theclass "selected"]
                      else li
+  let origPage s = if ":discuss" `isSuffixOf` s then take (length s - 8) s else s
   let linkForTab HistoryTab = Just $ tabli HistoryTab << anchor ! [href $ urlForPage page ++ "?revision=" ++ revision ++ "&history"] << "history"
-      linkForTab ViewTab    = Just $ tabli ViewTab << anchor ! [href $ urlForPage page ++ if revision == "HEAD" then "" else "?revision=" ++ revision] << "view"
+      linkForTab ViewTab    = if isDiscussPage page
+                                 then Just $ tabli DiscussTab << anchor ! [href $ urlForPage $ origPage page] << "page"
+                                 else Just $ tabli ViewTab << anchor ! [href $ urlForPage page ++ if revision == "HEAD" then "" else "?revision=" ++ revision] << "view"
+      linkForTab DiscussTab = if isDiscussPage page
+                                 then Just $ tabli ViewTab << anchor ! [href $ urlForPage page] << "discuss"
+                                 else Just $ tabli DiscussTab << anchor ! [href $ urlForPage page ++ "?discuss"] << "discuss"
       linkForTab EditTab    = if isPage page
                                  then Just $ tabli EditTab << anchor ! [href $ urlForPage page ++ "?edit&revision=" ++ revision ++
                                               if revision == "HEAD" then "" else "&" ++ urlEncodeVars [("logMsg", "Revert to " ++ revision)]] <<
                                                 if revision == "HEAD" then "edit" else "revert"
                                  else Nothing
-  let buttons    = ulist ! [theclass "tabs"] << mapMaybe linkForTab (pgTabs layout)
-  let userbox    = thediv ! [identifier "userbox"] <<
-                   case user of
-                        Just u   -> [ anchor ! [href ("/_logout?" ++ urlEncodeVars [("destination", page)])] << 
-                                    ("Logout " ++ u) ]
-                        Nothing  -> [ anchor ! [href ("/_login?" ++ urlEncodeVars [("destination", page)])] <<
-                                     "Login"
-                                    , primHtml "&nbsp;&bull;&nbsp;"
-                                    , anchor ! [href ("/_register?" ++ urlEncodeVars [("destination", page)])] <<
-                                      "Get an account" ]
+  let tabs = ulist ! [theclass "tabs"] << mapMaybe linkForTab (pgTabs layout)
+  let searchbox = gui ("/_search") ! [identifier "searchform"] <<
+                         [ textfield "patterns"
+                         , submit "search" "Search" ]
   let messages = pMessages params
   let htmlMessages = if null messages
                         then noHtml
                         else ulist ! [theclass "messages"] << map (li <<) messages
-  let body' = body << thediv ! [identifier "container"] <<
-                        [ thediv ! [identifier "sidebar"] <<
-                          [ thediv ! [identifier "logo"] << 
-                              anchor ! [href "/", title "Go to top page"] <<
-                                case wikiLogo cfg of
-                                     Nothing -> noHtml
-                                     Just f  -> image ! [src f]
-                          , sitenav
-                          , tools ]
-                        , userbox
-                        , thediv ! [identifier "maincol"] <<
-                          [ thediv ! [theclass "pageControls"] << buttons
-                          , thediv ! [identifier "content"] << 
-                              [ anchor ! [href $ urlForPage page] << (h1 ! [theclass "pageTitle"] << pageTitle')
-                              , if revision == "HEAD"
-                                   then noHtml
-                                   else h2 ! [theclass "revision"] << ("Revision " ++ revision)
-                              , htmlMessages
-                              , htmlContents ]
-                          , thediv ! [identifier "footer"] << primHtml (wikiFooter cfg) ]
-                        ]
-  ok $ toResponse $ head' +++ body'
+  templ <- liftIO $ readIORef template
+  let filledTemp = T.render $
+                   setAttribute "pagetitle" pageTitle $
+                   setAttribute "javascripts" javascriptlinks $
+                   setAttribute "pagename" page $
+                   (case user of
+                         Just u     -> setAttribute "user" u
+                         Nothing    -> id) $
+                   (if isPage page then setAttribute "ispage" "true" else id) $
+                   (if pgShowPageTools layout then setAttribute "pagetools" "true" else id) $
+                   (if pPrintable params then setAttribute "printable" "true" else id) $
+                   (if pRevision params == "HEAD" then id else setAttribute "nothead" "true") $
+                   setAttribute "revision" revision $
+                   setAttribute "sha1" sha1 $
+                   setAttribute "searchbox" (renderHtmlFragment searchbox) $
+                   setAttribute "exportbox" (renderHtmlFragment $  exportBox page params) $
+                   setAttribute "tabs" (renderHtmlFragment tabs) $
+                   setAttribute "messages" (renderHtmlFragment htmlMessages) $
+                   setAttribute "content" (renderHtmlFragment htmlContents) $
+                   templ
+  ok $ setContentType "text/html" $ toResponse filledTemp
 
 -- user authentication
 loginForm :: Params -> Html
@@ -1120,7 +1103,7 @@
 
 textToPandoc :: String -> Pandoc
 textToPandoc = readMarkdown (defaultParserState { stateSanitizeHTML = True, stateSmart = True }) .
-               filter (/= '\r') .  decodeString
+               filter (/= '\r')
 
 pageAsPandoc :: String -> Params -> Web (Maybe Pandoc)
 pageAsPandoc page params = do
diff --git a/Gitit/State.hs b/Gitit/State.hs
--- a/Gitit/State.hs
+++ b/Gitit/State.hs
@@ -38,10 +38,8 @@
 data Config = Config {
   repositoryPath  :: FilePath,                 -- path of git repository for pages
   userFile        :: FilePath,                 -- path of users database 
+  templateFile    :: FilePath,                 -- path of page template file
   staticDir       :: FilePath,                 -- path of static directory
-  wikiLogo        :: Maybe String,             -- Nothing, or Just path to an image to be displayed at top left
-  wikiTitle       :: String,                   -- title of wiki 
-  wikiFooter      :: String,                   -- HTML to be included at bottom of pages
   tableOfContents :: Bool,                     -- should each page have an automatic table of contents?
   maxUploadSize   :: Integer,                  -- maximum size of pages and file uploads
   portNumber      :: Int,                      -- port number to serve content on
@@ -59,10 +57,8 @@
 defaultConfig = Config {
   repositoryPath  = "wikidata",
   userFile        = "gitit-users",
+  templateFile    = "template.html",
   staticDir       = "static",
-  wikiLogo        = Just "/img/logo.png",
-  wikiTitle       = "Wiki",
-  wikiFooter      = "powered by <a href=\"http://github.com/jgm/gitit/tree/master/\">gitit</a>",
   tableOfContents = True,
   maxUploadSize   = 100000,
   portNumber      = 5001,
diff --git a/README.markdown b/README.markdown
--- a/README.markdown
+++ b/README.markdown
@@ -72,7 +72,8 @@
 
 Switch to the directory where you want to run gitit.  This should be a directory
 where you have write access, since two directories, `static` and `wikidata`, and
-a file, `gitit-users`, will be created here.  To start gitit, just type:
+two files, `gitit-users` and `template.html`, will be created here. To
+start gitit, just type:
 
     gitit
 
@@ -80,7 +81,9 @@
 
  1.  Create a git repository, `wikidata`, and add a default front page.
  2.  Create a `static` directory containing the scripts and CSS used by gitit.
- 3.  Start a web server on port 5001.
+ 3.  Create a `template.html` file containing an (HStringTemplate) template
+     for wiki pages.
+ 4.  Start a web server on port 5001.
 
 Check that it worked:  open a web browser and go to http://localhost:5001.
 
@@ -93,10 +96,8 @@
     Config {
     repositoryPath  = "wikidata",
     userFile        = "gitit-users",
+    templateFile    = "template.html",
     staticDir       = "static",
-    wikiLogo        = Just "/img/logo.png",
-    wikiTitle       = "Wiki",
-    wikiFooter      = "Powered by Gitit\n<!-- Logo courtesy of http://flickr.com/photos/wolfhound/127936545/, licensed under Creative Commons Attribution license -->",
     tableOfContents = False,
     maxUploadSize   = 100000,
     portNumber      = 5001,
@@ -112,24 +113,20 @@
   the wiki's pages will be stored.  If it does not exist, gitit will create
   it on startup.
 
-- `gitit-users` is a file containing user login information (with hashed
+- `userFile` is a file containing user login information (with hashed
   passwords).  If it does not exist, gitit will start with an empty list
-  of users.  Gitit will write a new `gitit-users` file on shutdown.
+  of users.  Gitit will write a new `userFile` on shutdown.
 
+- `templateFile` is a file containing an HTML template for the wiki pages.
+  If it does not exist, gitit will create a default template.  (For most
+  purposes, this can be used just as it is, but some users may wish to
+  customize the look of their wiki.)  `templateFile` is an
+  `HStringTemplate` template.
+
 - `staticDir` is the (relative) path of a directory in which static content
   (javascript, CSS, images) is stored.  If it does not exist, gitit will
   create it on startup.
 
-- `wikiLogo` is either `Nothing` (no logo) or `Just "/url/of/logo"`.
-  By default, the logo is set to `/img/logo.png`, so the easiest way to
-  change the logo is just to copy a new file to `static/img/logo.png`.
-  Be sure to use an absolute URL.
-
-- `wikiTitle` is the title that will be shown on the browser's title bar,
-  together with the name of the page being viewed.
-
-- `wikiFooter` is raw HTML that will be inserted in the footer of every page.
-
 - `tableOfContents` is either `False` or `True`.  If it is `True`, a table
   of contents (derived from the page's headers) will appear on each page.
 
@@ -176,6 +173,9 @@
 
 To change the look of the wiki, modify `screen.css` in `static/css`.
 To change the look of printed pages, modify `print.css`.
+The logo picture can be changed by copying a new PNG file to
+`static/img/logo.png`. For more radical changes, one can modify
+`template.html`.
 
 Adding support for math
 -----------------------
diff --git a/css/print.css b/css/print.css
--- a/css/print.css
+++ b/css/print.css
@@ -19,7 +19,7 @@
 h3{font-size:15pt;}
 h4,h5,h6{font-size:12pt;}
 
-code { font: 10pt Courier, monospace; } 
+pre, code { font: 10pt Courier, monospace; } 
 blockquote { margin: 1.3em; padding: 1em;  font-size: 10pt; }
 hr { background-color: #ccc; }
 
@@ -28,14 +28,13 @@
 a img { border: none; }
 
 /* Links */
-a:link, a:visited { background: transparent; font-weight: 700; text-decoration: underline;color:#333; }
-a:link[href^="http://"]:after, a[href^="http://"]:visited:after { content: " (" attr(href) ") "; font-size: 90%; }
+a:link, a:visited { background: transparent; font-weight: normal; text-decoration: underline; color:#333; }
+a:link[href^="http://"]:after, a[href^="http://"]:visited:after { content: " (" attr(href) ")"; font-size: 90%; }
 a[href^="http://"] {color:#000; }
 
 /* Table */
 table { margin: 1px; text-align:left; }
-th { border-bottom: 1px solid #333;  font-weight: bold; }
-td { border-bottom: 1px solid #333; }
+th { font-weight: bold; }
 th,td { padding: 4px 10px 4px 0; }
 tfoot { font-style: italic; }
 caption { background: #fff; margin-bottom:2em; text-align:left; }
@@ -46,8 +45,9 @@
 
 #maincol { margin-left: 1em; margin-right: 1em; border: none; }
 #content { border: none; }
-#sidebar, div.pageControls, #userbox, #footer {display:none;}
+#sidebar, #userbox, #footer {display:none;}
 #toc { display: none; }
 h1 a:link, h2 a:link, h3 a:link, h4 a:link, h5 a:link, h6 a:link { text-decoration: none; }
 h1.pageTitle { font-size: 220%; }
 td.lineNumbers { display: none; }
+ul.tabs { display: none; }
diff --git a/css/screen.css b/css/screen.css
--- a/css/screen.css
+++ b/css/screen.css
@@ -32,8 +32,9 @@
 /* QUOTES */
 
 blockquote { border-top: 1px solid #ccc; border-bottom: 1px solid #ccc; color: #666; }
-blockquote *:first-child:before { content: "\201C"; }
-blockquote *:first-child:after { content: "\201D"; }
+/* Uncomment these lines to have quotation marks inserted around block quotes: */
+/* blockquote *:first-child:before { content: "\201C"; } */
+/* blockquote *:first-child:after { content: "\201D"; } */
 
 /* FORMS */
 
@@ -158,60 +159,6 @@
 .success a {color:#264409; background:none; padding:0; margin:0; }
 .center {text-align: center;}
 
-/*---------STYLES FOR BUTTONS----------*/
-/* Demo: particletree.com/features/rediscovering-the-button-element */
-/*
-	<button type="submit" class="button positive">
-	  <img src="css/blueprint/plugins/buttons/icons/tick.png" alt=""/> Save
-	</button>
-
-	<a class="button" href="/password/reset/">
-	  <img src="css/blueprint/plugins/buttons/icons/key.png" alt=""/> Change Password
-	</a>
-
-	<a href="#" class="button negative">
-	  <img src="css/blueprint/plugins/buttons/icons/cross.png" alt=""/> Cancel
-	</a>
-*/
-
-a.button, button {
-  margin:0 0.583em 0.667em 0;
-  padding:5px 10px 5px 7px;   /* Links */
-  border:1px solid #dedede;
-  border-top:1px solid #eee;
-  border-left:1px solid #eee;
-  background-color:#f5f5f5;
-  font-family:"Lucida Grande", Tahoma, Arial, Verdana, sans-serif;
-  font-size:100%;
-  line-height:130%;
-  text-decoration:none;
-  font-weight:bold;
-  color:#565656;
-  cursor:pointer;
-}
-button {
-  width:auto;
-  overflow:visible;
-  padding:4px 10px 3px 7px;   /* IE6 */
-}
-button[type] {
-  padding:4px 10px 4px 7px;   /* Firefox */
-  line-height:17px;           /* Safari */
-}
-
-*:first-child+html  button[type] {
-  padding:4px 10px 3px 7px;   /* IE7 */
-}
-
-button img,  a.button img{
-  margin:0 3px -3px 0 !important;
-  padding:0;
-  border:none;
-  width:16px;
-  height:16px;
-  float:none;
-}
-
 input.search_term { width: 95% }
 
 /* Standard Buttons */
@@ -226,6 +173,35 @@
   color:#fff;
 }
 
+/* Link icons */
+
+/* Use this class if a link gets an icon when it shouldn't. */
+body a.noicon { background:none; padding:0; margin:0; }
+
+/* Make sure the icons are not cut */
+a[href^="http:"], a[href^="mailto:"],
+a[href$=".pdf"], a[href$=".doc"], a[href$=".xls"], a[href$=".rss"],
+a[href$=".rdf"], a[href^="aim:"] {
+  padding:2px 22px 2px 0;
+  margin:-2px 0;
+  background-repeat: no-repeat;
+  background-position: right center;
+}
+
+/* External links */
+a[href^="http:"]          { background-image: url(../img/icons/external.png); padding-right: 14px; }
+a[href^="mailto:"]        { background-image: url(../img/icons/email.png); }
+
+/* Files */
+a[href$=".pdf"]   { background-image: url(../img/icons/pdf.png); }
+a[href$=".doc"]   { background-image: url(../img/icons/doc.png); }
+a[href$=".xls"]   { background-image: url(../img/icons/xls.png); }
+
+/* Misc */
+a[href$=".rss"],
+a[href$=".rdf"]   { background-image: url(../img/icons/feed.png); }
+a[href^="aim:"]   { background-image: url(../img/icons/im.png); }
+
 h1 > a:link, h1 > a:active, h1 > a:hover, h1 > a:focus, h1 > a:visited,
 h2 > a:link, h2 > a:active, h2 > a:hover, h2 > a:focus, h2 > a:visited,
 h3 > a:link, h3 > a:active, h3 > a:hover, h3 > a:focus, h3 > a:visited,
@@ -269,9 +245,8 @@
 #sidebar ul li { color: #888; }
 #maincol { position: absolute; margin-left: 13em; margin-top: 1em; padding-top: 0; margin-right: 0; }
 #content { border: 1px solid #ccc; background-color: #fff; padding: 1em; font-size: 110%; line-height: 150%; }
-#pageControls { }
 div#toc {
-  float: right; 
+  float: right;
   background-color: #f9f9f9;
   border: 10px solid white;
   margin: 0.8em;
@@ -311,7 +286,7 @@
     line-height: 1.2em;
 }
 ul.tabs li.selected {
-    border-bottom: 1px solid white;
+    border-bottom: 3px solid white;
 }
 ul.tabs li a {
     text-decoration: none;
@@ -392,7 +367,7 @@
 .deleted { text-decoration: line-through; color: gray; }
 h2.revision {
   font-size: 100%;
-  color: #ccc;
+  color: #888;
   font-style: italic;
   border: none;
   margin: 0 0 0.5em 0;
diff --git a/data/FrontPage.page b/data/FrontPage.page
--- a/data/FrontPage.page
+++ b/data/FrontPage.page
@@ -8,7 +8,7 @@
 display TeX math and highlighted source code.
 
 You can edit this page by double-clicking on it, or by clicking on the
-"edit" button at the bottom of the screen.
+"edit" tab at the top of the screen.
 
 You can make a link to another wiki page like this:
 `[French Cheeses]()`.
@@ -21,7 +21,7 @@
 To create a new wiki page, just create a link to it and follow
 the link.
 
-Help is always available through the "help" link on the top bar.
+Help is always available through the "Help" link in the sidebar.
 
 [Wiki]: http://en.wikipedia.org/wiki/Wiki
 [git]: http://git.or.cz/
diff --git a/data/SampleConfig.hs b/data/SampleConfig.hs
--- a/data/SampleConfig.hs
+++ b/data/SampleConfig.hs
@@ -1,10 +1,8 @@
 Config {
 repositoryPath  = "wikidata",
 userFile        = "gitit-users",
+templateFile    = "template.html",
 staticDir       = "static",
-wikiLogo        = Just "/img/logo.png",
-wikiTitle       = "Wiki",
-wikiFooter      = "Powered by Gitit\n<!-- Logo courtesy of http://flickr.com/photos/wolfhound/127936545/, licensed under Creative Commons Attribution license -->",
 tableOfContents = False,
 maxUploadSize   = 100000,
 portNumber      = 5001,
diff --git a/data/template.html b/data/template.html
new file mode 100644
--- /dev/null
+++ b/data/template.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>Wiki - $pagetitle$</title>
+    $if(printable)$
+    <link href="/css/print.css" rel="stylesheet" media="all" type= "text/css" />
+    $else$
+    <link href="/css/screen.css" rel="stylesheet" media="screen, projection" type="text/css" />
+    <link href="/css/hk-pyg.css" rel="stylesheet" media="screen, projection" type="text/css" />
+    <link href="/css/print.css" rel="stylesheet" media="print" type= "text/css" />
+    $endif$
+    <!--[if IE]><link href="/css/ie.css" rel="stylesheet" media="screen, projection" type="text/css" /><![endif]-->
+    $javascripts$
+  </head>
+  <body>
+    <div id="container">
+      <div id="sidebar">
+        <div id="logo">
+          <a href="/" title="Go to top page"><img src="/img/logo.png" /></a>
+        </div>
+        <div class="sitenav">
+          <fieldset>
+            <legend>Site</legend>
+            <ul>
+              <li><a href="/">Front page</a></li>
+              <li><a href="/_index">All pages</a></li>
+              <li><a href="/_random">Random page</a></li>
+              <li><a href="/_activity">Recent activity</a></li>
+              <li><a href="/_upload">Upload a file</a></li>
+              <li><a href="/Help">Help</a></li>
+            </ul>
+            $searchbox$
+          </fieldset>
+        </div>
+        $if(pagetools)$
+        <div class="pageTools">
+          <fieldset>
+            <legend>This page</legend>
+            <ul>
+              <li><a href="/$pagename$?revision=$revision$&amp;showraw">Raw page source</a></li>
+              <li><a href="/$pagename$?revision=$revision$&amp;printable">Printable version</a></li>
+              <li><a href="/$pagename$?revision=$sha1$">Permanent link</a></li>
+              <li><a href="/$pagename$?delete">Delete this page</a></li>
+            </ul>
+            $exportbox$
+          </fieldset>
+        </div>
+        $endif$
+      </div>
+      <div id="userbox">
+        $if(user)$
+        <a href="/_logout?destination=$pagename$">Logout $user$</a>
+        $else$
+        <a href="/_login?destination=$pagename$">Login</a> &bull; <a href="/_register?destination=$pagename$">Get an account</a>
+        $endif$
+      </div>
+      <div id="maincol">
+        $tabs$ 
+        <div id="content">
+          $if(nothead)$
+          <h2 class="revision">Revision $revision$</h2>
+          $endif$
+          <h1 class="pageTitle"><a href="/$pagename$">$pagetitle$</a></h1>
+          $messages$
+          <div id="wikipage" ondblclick="window.location='/$pagename$?edit">
+            $content$
+          </div>
+        </div>
+        <div id="footer">powered by <a href="http://github.com/jgm/gitit/tree/master/">gitit</a></div>
+      </div>
+    </div>
+  </body>
+</html>
diff --git a/gitit.cabal b/gitit.cabal
--- a/gitit.cabal
+++ b/gitit.cabal
@@ -1,5 +1,5 @@
 name:                gitit
-version:             0.3.1
+version:             0.3.2
 Cabal-version:       >= 1.2
 build-type:          Simple
 synopsis:            Wiki using HAppS, git, and pandoc.
@@ -34,6 +34,7 @@
                      js/jquery-ui.packed.js,
                      js/preview.js, js/search.js,
                      data/post-update, data/FrontPage.page, data/Help.page,
+                     data/template.html,
                      README.markdown, data/SampleConfig.hs, BLUETRIP-LICENSE,
                      TANGOICONS
 
@@ -46,7 +47,8 @@
                      network, old-time, highlighting-kate, bytestring,
                      utf8-string, HAppS-Server >= 0.9.3 && < 0.9.4,
                      HAppS-State >= 0.9.3 && < 0.9.4,
-                     HAppS-Data >= 0.9.3 && < 0.9.4, SHA > 1, HTTP
+                     HAppS-Data >= 0.9.3 && < 0.9.4, SHA > 1, HTTP,
+                     HStringTemplate
   if impl(ghc >= 6.10)
     build-depends:   base >= 4, syb
   ghc-options:       -Wall -threaded
