diff --git a/CHANGES b/CHANGES
--- a/CHANGES
+++ b/CHANGES
@@ -1,7 +1,54 @@
 User-visible changes in hledger-web.
 See also hledger's change log.
 
-0.27 (unreleased)
+
+# 1.0 (2016/10/26)
+
+## ui
+
+-   show the sidebar by default (#310)
+
+-   fix the add link's tooltip
+
+-   when the add form opens, focus the first field (#338)
+
+-   leave the add form's date field blank, avoiding a problem with tab clearing it (#322)
+
+-   use transaction id instead of date in transaction urls (#308) (Thomas R. Koll)
+
+-   after following a link to a transaction, highlight it (Thomas R. Koll)
+
+-   misc. HTML/CSS/file cleanups/fixes (Thomas R. Koll)
+
+-   added .btn-default for consistent button styling across browsers (#418) (Dominik Süß)
+
+## misc
+
+-   startup is more robust (#226).
+
+    Now we exit if something is already using the specified port,
+    and we don't open a browser page before the app is ready.
+
+-   termination is more robust, avoiding stray background threads.
+
+    We terminate the server thread more carefully on exit, eg on control-C in GHCI.
+
+-   more robust register dates and filtering in some situations (see hledger-ui notes)
+
+-   reloading the journal preserves options, arguments in effect (#314).
+
+    The initial query specified by command line arguments is now preserved
+    when the journal is reloaded. This does not appear in the web UI, it's
+    like an invisible extra filter.
+
+-   show a proper not found page on 404
+
+-   document the special \`inacct:\` query (#390)
+
+
+
+
+0.27 (2015/10/30)
 
 - Fix keyboard shortcut for adding a transaction (Carlos Lopez-Camey)
 
diff --git a/Foundation.hs b/Foundation.hs
--- a/Foundation.hs
+++ b/Foundation.hs
@@ -124,12 +124,11 @@
     --     hamletToRepHtml $(hamletFile "templates/default-layout-wrapper.hamlet")
 
         pc <- widgetToPageContent $ do
-            $(widgetFile "normalize")
             addStylesheet $ StaticR css_bootstrap_min_css
              -- load these things early, in HEAD:
             toWidgetHead [hamlet|
-                          <script type="text/javascript" src="@{StaticR js_jquery_min_js}"></script>
-                          <script type="text/javascript" src="@{StaticR js_typeahead_bundle_min_js}"></script>
+                          <script type="text/javascript" src="@{StaticR js_jquery_min_js}">
+                          <script type="text/javascript" src="@{StaticR js_typeahead_bundle_min_js}">
                          |]
             addScript $ StaticR js_bootstrap_min_js
             -- addScript $ StaticR js_typeahead_bundle_min_js
@@ -216,8 +215,8 @@
 -- | Make a ViewData using the given date and request parameters, and defaults elsewhere.
 viewdataWithDateAndParams :: Day -> String -> String -> String -> ViewData
 viewdataWithDateAndParams d q a p =
-    let (querymatcher,queryopts) = parseQuery d q
-        (acctsmatcher,acctsopts) = parseQuery d a
+    let (querymatcher,queryopts) = parseQuery d (pack q)
+        (acctsmatcher,acctsopts) = parseQuery d (pack a)
     in VD {
            opts         = defwebopts
           ,j            = nulljournal
@@ -230,57 +229,62 @@
           ,am           = acctsmatcher
           ,aopts        = acctsopts
           ,showpostings = p == "1"
-          ,showsidebar  = False
+          ,showsidebar  = True
           }
 
 -- | Gather data used by handlers and templates in the current request.
 getViewData :: Handler ViewData
 getViewData = do
-  app        <- getYesod
-  let opts@WebOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} = appOpts app
-  (j, merr)  <- getCurrentJournal app copts{reportopts_=ropts{no_elide_=True}}
-  lastmsg    <- getLastMessage
-  let msg = maybe lastmsg (Just . toHtml) merr
-  Just here  <- getCurrentRoute
-  today      <- liftIO getCurrentDay
-  q          <- getParameterOrNull "q"
-  a          <- getParameterOrNull "a"
-  p          <- getParameterOrNull "p"
-
-  -- a "sidebar" query parameter overrides the "showsidebar" cookie
-  sidebarparam <- lookupGetParam (pack "sidebar")
-  cookies <- reqCookies <$> getRequest
-  let sidebarcookie = lookup "showsidebar" cookies
-  let showsidebar = maybe (sidebarcookie == Just "1") (=="1") sidebarparam
+  mhere      <- getCurrentRoute
+  case mhere of
+    Nothing -> return nullviewdata
+    Just here -> do
+      app        <- getYesod
+      let opts@WebOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} = appOpts app
+      today      <- liftIO getCurrentDay
+      (j, merr)  <- getCurrentJournal app copts{reportopts_=ropts{no_elide_=True}} today
+      lastmsg    <- getLastMessage
+      let msg = maybe lastmsg (Just . toHtml) merr
+      q          <- getParameterOrNull "q"
+      a          <- getParameterOrNull "a"
+      p          <- getParameterOrNull "p"
+      -- sidebar visibility: show it, unless there is a showsidebar cookie
+      -- set to "0", or a ?sidebar=0 query parameter.
+      msidebarparam <- lookupGetParam "sidebar"
+      msidebarcookie <- reqCookies <$> getRequest >>= return . lookup "showsidebar"
+      let showsidebar = maybe (msidebarcookie /= Just "0") (/="0") msidebarparam
 
-  return (viewdataWithDateAndParams today q a p){
-               opts=opts
-              ,msg=msg
-              ,here=here
-              ,today=today
-              ,j=j
-              ,showsidebar=showsidebar
-              }
-    where
-      -- | Update our copy of the journal if the file changed. If there is an
-      -- error while reloading, keep the old one and return the error, and set a
-      -- ui message.
-      getCurrentJournal :: App -> CliOpts -> Handler (Journal, Maybe String)
-      getCurrentJournal app opts = do
-        -- XXX put this inside atomicModifyIORef' for thread safety
-        j <- liftIO $ readIORef $ appJournal app
-        (jE, changed) <- liftIO $ journalReloadIfChanged opts j
-        if not changed
-         then return (j,Nothing)
-         else case jE of
-                Right j' -> do liftIO $ writeIORef (appJournal app) j'
-                               return (j',Nothing)
-                Left e   -> do setMessage $ "error while reading" {- ++ ": " ++ e-}
-                               return (j, Just e)
+      return (viewdataWithDateAndParams today q a p){
+                   opts=opts
+                  ,msg=msg
+                  ,here=here
+                  ,today=today
+                  ,j=j
+                  ,showsidebar=showsidebar
+                  }
+        where
+          -- | Update our copy of the journal if the file changed. If there is an
+          -- error while reloading, keep the old one and return the error, and set a
+          -- ui message.
+          getCurrentJournal :: App -> CliOpts -> Day -> Handler (Journal, Maybe String)
+          getCurrentJournal app opts d = do
+            -- XXX put this inside atomicModifyIORef' for thread safety
+            j <- liftIO $ readIORef $ appJournal app
+            (ej, changed) <- liftIO $ journalReloadIfChanged opts d j
+            -- re-apply any initial filter specified at startup
+            let initq = queryFromOpts d $ reportopts_ opts
+                ej' = filterJournalTransactions initq <$> ej
+            if not changed
+             then return (j,Nothing)
+             else case ej' of
+                    Right j' -> do liftIO $ writeIORef (appJournal app) j'
+                                   return (j',Nothing)
+                    Left e   -> do setMessage $ "error while reading" {- ++ ": " ++ e-}
+                                   return (j, Just e)
 
-      -- | Get the named request parameter, or the empty string if not present.
-      getParameterOrNull :: String -> Handler String
-      getParameterOrNull p = unpack `fmap` fromMaybe "" <$> lookupGetParam (pack p)
+          -- | Get the named request parameter, or the empty string if not present.
+          getParameterOrNull :: String -> Handler String
+          getParameterOrNull p = unpack `fmap` fromMaybe "" <$> lookupGetParam (pack p)
 
 -- | Get the message that was set by the last request, in a
 -- referentially transparent manner (allowing multiple reads).
@@ -333,24 +337,31 @@
 
   });
 
-<form#addform method=POST style="position:relative;">
-  <table.form style="width:100%; white-space:nowrap;">
-   <tr>
-    <td colspan=4>
-     <table style="width:100%;">
-      <tr#descriptionrow>
-       <td>
-        <input #date        .typeahead .form-control .input-lg type=text size=15 name=date placeholder="Date" value=#{defdate}>
-       <td>
-        <input #description .typeahead .form-control .input-lg type=text size=40 name=description placeholder="Description">
-   $forall n <- postingnums
+<form#addform method=POST .form>
+ <div .form-group>
+  <div .row>
+   <div .col-md-2 .col-xs-6 .col-sm-6>
+    <input #date .typeahead .form-control .input-lg type=text size=15 name=date placeholder="Date" value="#{defdate}">
+   <div .col-md-10 .col-xs-6 .col-sm-6>
+    <input #description .typeahead .form-control .input-lg type=text size=40 name=description placeholder="Description">
+ <div .account-postings>
+  $forall n <- postingnums
     ^{postingfields vd n}
-  <span style="padding-left:2em;">
-   <span .small>
-     Tab in last field for <a .small href="#" onclick="addformAddPosting(); return false;">more</a> (or ctrl +, ctrl -)
+ <div .col-md-8 .col-xs-8 .col-sm-8>
+ <div .col-md-4 .col-xs-4 .col-sm-4>
+  <button type=submit .btn .btn-default .btn-lg name=submit>add
+ $if length filepaths > 1
+  <br>
+  <span class="input-lg">to:
+   ^{journalselect filepaths}
+ <span style="padding-left:2em;">
+  <span .small>
+    Enter a value in the last field for
+    <a href="#" onclick="addformAddPosting(); return false;">more
+    (or ctrl +, ctrl -)
 |]
  where
-  defdate = "today" :: String
+  defdate = "" :: String -- #322 don't set a default, typeahead(?) clears it on tab. See also hledger.js
   dates = ["today","yesterday","tomorrow"] :: [String]
   descriptions = sort $ nub $ map tdescription $ jtxns j
   accts = sort $ journalAccountNamesUsed j
@@ -358,36 +369,21 @@
   listToJsonValueObjArrayStr as  = preEscapedString $ escapeJSSpecialChars $ encode $ JSArray $ map (\a -> JSObject $ toJSObject [("value", showJSON a)]) as
   numpostings = 4
   postingnums = [1..numpostings]
+  filepaths = map fst $ jfiles j
   postingfields :: ViewData -> Int -> HtmlUrl AppRoute
   postingfields _ n = [hamlet|
-<tr .posting>
- <td style="padding-left:2em;">
-  <input ##{acctvar} .account-input .typeahead .form-control .input-lg style="width:100%;" type=text name=#{acctvar} placeholder="#{acctph}">
- ^{amtfieldorsubmitbtn}
+<div .form-group .row .account-group ##{grpvar}>
+ <div .col-md-8 .col-xs-8 .col-sm-8>
+  <input ##{acctvar} .account-input .typeahead .form-control .input-lg type=text name=#{acctvar} placeholder="#{acctph}">
+ <div .col-md-4 .col-xs-4 .col-sm-4>
+  <input ##{amtvar} .amount-input .form-control .input-lg type=text name=#{amtvar} placeholder="#{amtph}">
 |]
    where
-    islast = n == numpostings
     acctvar = "account" ++ show n
     acctph = "Account " ++ show n
-    amtfieldorsubmitbtn
-       | not islast = [hamlet|
-          <td>
-           <input ##{amtvar} .amount-input .form-control .input-lg type=text size=10 name=#{amtvar} placeholder="#{amtph}">
-         |]
-       | otherwise = [hamlet|
-          <td #addbtncell style="text-align:right;">
-           <button type=submit .btn .btn-lg name=submit>add
-           $if length filepaths > 1
-            <br>
-            <span class="input-lg">to:
-            ^{journalselect filepaths}
-         |]
-       where
-        amtvar = "amount" ++ show n
-        amtph = "Amount " ++ show n
-        filepaths = map fst $ files j
-
-           -- <button .btn style="font-size:18px;" type=submit title="Add this transaction">Add
+    amtvar = "amount" ++ show n
+    amtph = "Amount " ++ show n
+    grpvar = "grp" ++ show n
 
 journalselect :: [FilePath] -> HtmlUrl AppRoute
 journalselect journalfilepaths = [hamlet|
diff --git a/Handler/AddForm.hs b/Handler/AddForm.hs
--- a/Handler/AddForm.hs
+++ b/Handler/AddForm.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE CPP, FlexibleContexts, OverloadedStrings, QuasiQuotes, RecordWildCards #-}
+{-# LANGUAGE CPP, FlexibleContexts, OverloadedStrings, QuasiQuotes, RecordWildCards, TypeFamilies #-}
 -- | Add form data & handler. (The layout and js are defined in
 -- Foundation so that the add form can be in the default layout for
 -- all views.)
@@ -10,13 +10,14 @@
 #if !MIN_VERSION_base(4,8,0)
 import Control.Applicative
 #endif
+import Control.Monad.State.Strict (evalStateT)
 import Data.Either (lefts,rights)
 import Data.List (sort)
 import qualified Data.List as L (head) -- qualified keeps dev & prod builds warning-free
 import Data.Text (append, pack, unpack)
 import qualified Data.Text as T
 import Data.Time.Calendar
-import Text.Parsec (digit, eof, many1, string, runParser)
+import Text.Megaparsec (digitChar, eof, some, string, runParser, ParseError, Dec)
 
 import Hledger.Utils
 import Hledger.Data hiding (num)
@@ -43,7 +44,6 @@
                     $forall e<-errs
                      \#{e}<br>
                    |]
-                                
   -- 1. process the fixed fields with yesod-form
 
   VD{..} <- getViewData
@@ -55,7 +55,7 @@
 
       validateDate :: Text -> Handler (Either FormMessage Day)
       validateDate s = return $
-        case fixSmartDateStrEither' today $ strip $ unpack s of
+        case fixSmartDateStrEither' today $ T.pack $ strip $ unpack s of
           Right d  -> Right d
           Left _   -> Left $ MsgInvalidEntry $ pack "could not parse date \"" `append` s `append` pack "\":" -- ++ show e)
 
@@ -83,11 +83,11 @@
       let numberedParams s =
             reverse $ dropWhile (T.null . snd) $ reverse $ sort
             [ (n,v) | (k,v) <- params
-                    , let en = parsewith (paramnamep s) $ T.unpack k
+                    , let en = parsewith (paramnamep s) k :: Either (ParseError Char Dec) Int
                     , isRight en
                     , let Right n = en
                     ]
-            where paramnamep s = do {string s; n <- many1 digit; eof; return (read n :: Int)}
+            where paramnamep s = do {string s; n <- some digitChar; eof; return (read n :: Int)}
           acctparams = numberedParams "account"
           amtparams  = numberedParams "amount"
           num = length acctparams
@@ -95,8 +95,8 @@
                     | map fst acctparams == [1..num] &&
                       map fst amtparams `elem` [[1..num], [1..num-1]] = []
                     | otherwise = ["the posting parameters are malformed"]
-          eaccts = map (parsewith (accountnamep <* eof) . strip . T.unpack . snd) acctparams
-          eamts  = map (runParser (amountp <* eof) nullctx "" . strip . T.unpack . snd) amtparams
+          eaccts = map (runParser (accountnamep <* eof) "" . textstrip  . snd) acctparams
+          eamts  = map (runParser (evalStateT (amountp <* eof) mempty) "" . textstrip . snd) amtparams
           (accts, acctErrs) = (rights eaccts, map show $ lefts eaccts)
           (amts', amtErrs)  = (rights eamts, map show $ lefts eamts)
           amts | length amts' == num = amts'
@@ -106,7 +106,7 @@
                | otherwise = either (\e -> Left [L.head $ lines e]) Right
                               (balanceTransaction Nothing $ nulltransaction {
                                   tdate=date
-                                 ,tdescription=desc
+                                 ,tdescription=T.pack desc
                                  ,tpostings=[nullposting{paccount=acct, pamount=Mixed [amt]} | (acct,amt) <- zip accts amts]
                                  })
       case etxn of
diff --git a/Handler/Common.hs b/Handler/Common.hs
--- a/Handler/Common.hs
+++ b/Handler/Common.hs
@@ -6,8 +6,8 @@
 
 import Import
 
-import Data.List
-import Data.Text(pack)
+-- import Data.Text (Text)
+import qualified Data.Text as T
 import Data.Time.Calendar
 import System.FilePath (takeFileName)
 #if BLAZE_HTML_0_4
@@ -33,55 +33,49 @@
   defaultLayout $ do
       setTitle $ toHtml $ title ++ " - hledger-web"
       toWidget [hamlet|
-        <div#content>
-         $if showsidebar vd
-          <div#sidebar>
-           <div#sidebar-spacer>
-           <div#sidebar-body>
-            ^{sidebar vd}
-         $else
-          <div#sidebar style="display:none;">
-           <div#sidebar-spacer>
-           <div#sidebar-body>
-         <div#main>
-          ^{topbar vd}
-          <div#maincontent>
-           ^{searchform vd}
-           ^{content}
+         ^{topbar vd}
+         ^{sidebar vd}
+         <div #main-content .col-xs-12 .#{showmd} .#{showsm}>
+          ^{searchform vd}
+          ^{content}
       |]
+  where
+    showmd = if showsidebar vd then "col-md-8" else "col-md-12" :: String
+    showsm = if showsidebar vd then "col-sm-8" else "col-sm-12" :: String
 
 -- | Global toolbar/heading area.
 topbar :: ViewData -> HtmlUrl AppRoute
 topbar VD{..} = [hamlet|
-<nav class="navbar" role="navigation">
- <div#topbar>
-  <h1>#{title}
+<div#spacer .#{showsm} .#{showmd} .col-xs-2>
+ <h1>
+  <button .visible-xs .btn .btn-default type="button" data-toggle="offcanvas">
+   <span .glyphicon .glyphicon-align-left .tgl-icon>
+<div#topbar .col-md-8 .col-sm-8 .col-xs-10>
+ <h1>#{title}
+
 |]
   where
     title = takeFileName $ journalFilePath j
+    showmd = if showsidebar then "col-md-4" else "col-any-0" :: String
+    showsm = if showsidebar then "col-sm-4" else "" :: String
 
 -- | The sidebar used on most views.
 sidebar :: ViewData -> HtmlUrl AppRoute
 sidebar vd@VD{..} =
  [hamlet|
- <a href=@{JournalR} title="Go back to top">
-  hledger-web
-
- <p>
- <!--
- <a#sidebartogglebtn role="button" style="cursor:pointer;" onclick="sidebarToggle()" title="Show/hide sidebar">
-  <span class="glyphicon glyphicon-expand"></span>
- -->
- <br>
- <div#sidebar-content>
-  <p style="margin-top:1em;">
-   <a href=@{JournalR} .#{journalcurrent} title="Show general journal entries, most recent first" style="white-space:nowrap;">Journal
-  <div#accounts style="margin-top:1em;">
+ <div #sidebar-menu .#{showmd} .#{showsm} .sidebar-offcanvas>
+  <table .main-menu .table>
+   <tr .#{journalcurrent}>
+    <td .top .acct>
+     <a href=@{JournalR} .#{journalcurrent} title="Show general journal entries, most recent first">Journal
+    <td .top>
    ^{accounts}
 |]
  where
-  journalcurrent = if here == JournalR then "current" else "" :: String
+  journalcurrent = if here == JournalR then "inacct" else "" :: String
   accounts = balanceReportAsHtml opts vd $ balanceReport (reportopts_ $ cliopts_ opts){empty_=True} am j
+  showmd = if showsidebar then "col-md-4" else "col-any-0" :: String
+  showsm = if showsidebar then "col-sm-4" else "" :: String
 
 -- -- | Navigation link, preserving parameters and possibly highlighted.
 -- navlink :: ViewData -> String -> AppRoute -> String -> HtmlUrl AppRoute
@@ -104,17 +98,18 @@
 -- | Search form for entering custom queries to filter journal data.
 searchform :: ViewData -> HtmlUrl AppRoute
 searchform VD{..} = [hamlet|
-<div#searchformdiv>
- <form#searchform.form method=GET>
-  <table width="100%">
-   <tr>
-    <td width="99%" style="position:relative;">
-     $if filtering
-      <a role=button .btn .close style="position:absolute; right:0; padding-right:.1em; padding-left:.1em; margin-right:.1em; margin-left:.1em; font-size:24px;" href="@{here}" title="Clear search terms">&times;
-     <input .form-control style="font-size:18px; padding-bottom:2px;" name=q value=#{q} title="Enter hledger search patterns to filter the data below">
-    <td width="1%" style="white-space:nowrap;">
-     <button .btn style="font-size:18px;" type=submit title="Apply search terms">Search
-     <button .btn style="font-size:18px;" type=button data-toggle="modal" data-target="#helpmodal" title="Show search and general help">?
+<div#searchformdiv .row>
+ <form#searchform .form-inline method=GET>
+  <div .form-group .col-md-12 .col-sm-12 .col-xs-12>
+    <div #searchbar .input-group>
+     <input .form-control name=q value=#{q} title="Enter hledger search patterns to filter the data below" placeholder="Search">
+     <div .input-group-btn>
+      $if filtering
+       <a href=@{here} .btn .btn-default title="Clear search terms">
+        <span .glyphicon .glyphicon-remove-circle>
+      <button .btn .btn-default type=submit title="Apply search terms">
+       <span .glyphicon .glyphicon-search>
+      <button .btn .btn-default type=button data-toggle="modal" data-target="#helpmodal" title="Show search and general help">?
 |]
  where
   filtering = not $ null q
@@ -138,7 +133,7 @@
 --   <tr#addbuttonrow>
 --    <td>
 --     <span.help>^{formathelp}
---    <td align=right>
+--    <td>
 --     <span.help>
 --      Are you sure ? This will overwrite the journal. #
 --     <input type=hidden name=action value=edit>
@@ -183,55 +178,46 @@
 balanceReportAsHtml :: WebOpts -> ViewData -> BalanceReport -> HtmlUrl AppRoute
 balanceReportAsHtml _ vd@VD{..} (items',total) =
  [hamlet|
- <table.balancereport>
-  <tr>
-   <td>Account
-   <td style="padding-left:1em; text-align:right;">Balance
   $forall i <- items
    ^{itemAsHtml vd i}
-  <tr.totalrule>
-   <td colspan=2>
-  <tr>
+  <tr .total>
    <td>
-   <td.balance align=right>#{mixedAmountAsHtml total}
+   <td>
+    #{mixedAmountAsHtml total}
 |]
  where
    l = ledgerFromJournal Any j
    inacctmatcher = inAccountQuery qopts
    items = items' -- maybe items' (\m -> filter (matchesAccount m . \(a,_,_,_)->a) items') showacctmatcher
    itemAsHtml :: ViewData -> BalanceReportItem -> HtmlUrl AppRoute
-   itemAsHtml _ ((acct, adisplay, aindent), abal) = [hamlet|
-<tr.item.#{inacctclass}>
- <td.account.#{depthclass}>
-  \#{indent}
-   <a href="@?{acctquery}" title="Show transactions affecting this account and subaccounts">#{adisplay}
-   <span.hoverlinks>
-    $if hassubs
-     &nbsp;
-     <a href="@?{acctonlyquery}" title="Show transactions affecting this account but not subaccounts">only
-
- <td.balance align=right>#{mixedAmountAsHtml abal}
+   itemAsHtml _ (acct, adisplay, aindent, abal) = [hamlet|
+<tr .#{inacctclass}>
+ <td .acct>
+  <div .ff-wrapper>
+   \#{indent}
+   <a href="@?{acctquery}" .#{inacctclass} title="Show transactions affecting this account and subaccounts">#{adisplay}
+   $if hassubs
+    <a href="@?{acctonlyquery}" .only .hidden-sm .hidden-xs title="Show transactions affecting this account but not subaccounts">only
+ <td>
+  #{mixedAmountAsHtml abal}
 |]
      where
        hassubs = not $ maybe False (null.asubs) $ ledgerAccount l acct
- -- <td.numpostings align=right title="#{numpostings} transactions in this account">(#{numpostings})
-       -- numpostings = maybe 0 (length.apostings) $ ledgerAccount l acct
-       depthclass = "depth"++show aindent
        inacctclass = case inacctmatcher of
-                       Just m' -> if m' `matchesAccount` acct then "inacct" else "notinacct"
+                       Just m' -> if m' `matchesAccount` acct then "inacct" else ""
                        Nothing -> "" :: String
        indent = preEscapedString $ concat $ replicate (2 * (1+aindent)) "&nbsp;"
-       acctquery = (RegisterR, [("q", pack $ accountQuery acct)])
-       acctonlyquery = (RegisterR, [("q", pack $ accountOnlyQuery acct)])
+       acctquery = (RegisterR, [("q", T.pack $ accountQuery acct)])
+       acctonlyquery = (RegisterR, [("q", T.pack $ accountOnlyQuery acct)])
 
 accountQuery :: AccountName -> String
-accountQuery a = "inacct:" ++ quoteIfSpaced a -- (accountNameToAccountRegex a)
+accountQuery a = "inacct:" ++ T.unpack (quoteIfSpaced a) -- (accountNameToAccountRegex a)
 
 accountOnlyQuery :: AccountName -> String
-accountOnlyQuery a = "inacctonly:" ++ quoteIfSpaced a -- (accountNameToAccountRegex a)
+accountOnlyQuery a = "inacctonly:" ++ T.unpack (quoteIfSpaced a ) -- (accountNameToAccountRegex a)
 
 accountUrl :: AppRoute -> AccountName -> (AppRoute, [(Text, Text)])
-accountUrl r a = (r, [("q", pack $ accountQuery a)])
+accountUrl r a = (r, [("q", T.pack $ accountQuery a)])
 
 -- stringIfLongerThan :: Int -> String -> String
 -- stringIfLongerThan n s = if length s > n then s else ""
@@ -251,8 +237,8 @@
           (prevdy,prevdm,_) = toGregorian prevd
 
 mixedAmountAsHtml :: MixedAmount -> Html
-mixedAmountAsHtml b = preEscapedString $ addclass $ intercalate "<br>" $ lines $ showMixedAmountWithoutPrice b
-    where addclass = printf "<span class=\"%s\">%s</span>" (c :: String)
+mixedAmountAsHtml b = preEscapedString $ unlines $ map addclass $ lines $ showMixedAmountWithoutPrice b
+    where addclass = printf "<span class=\"%s\">%s</span><br/>" (c :: String)
           c = case isNegativeMixedAmount b of Just True -> "negative amount"
                                               _         -> "positive amount"
 
diff --git a/Handler/JournalR.hs b/Handler/JournalR.hs
--- a/Handler/JournalR.hs
+++ b/Handler/JournalR.hs
@@ -3,7 +3,8 @@
 
 module Handler.JournalR where
 
-import Data.Text (pack)
+-- import Data.Text (Text)
+import qualified Data.Text as T
 import Import
 
 import Handler.AddForm
@@ -27,16 +28,18 @@
       -- showlastcolumn = if injournal && not filtering then False else True
       title = case inacct of
                 Nothing       -> "General Journal"++s2
-                Just (a,inclsubs) -> "Transactions in "++a++s1++s2
+                Just (a,inclsubs) -> "Transactions in "++T.unpack a++s1++s2
                                       where s1 = if inclsubs then "" else " (excluding subaccounts)"
                 where
                   s2 = if filtering then ", filtered" else ""
       maincontent = journalTransactionsReportAsHtml opts vd $ journalTransactionsReport (reportopts_ $ cliopts_ opts) j m
   hledgerLayout vd "journal" [hamlet|
-       <h2#contenttitle>#{title}
-       <!-- p>Journal entries record movements of commodities between accounts. -->
-       <a#addformlink role="button" style="cursor:pointer;" onClick="addformReset(true);" data-toggle="modal" data-target="#addmodal"  title="Add a new transaction to the journal" style="margin-top:1em;">Add a transaction
-       ^{maincontent}
+       <div .row>
+        <h2 #contenttitle>#{title}
+        <!-- p>Journal entries record movements of commodities between accounts. -->
+        <a #addformlink role="button" style="cursor:pointer; margin-top:1em;" data-toggle="modal" data-target="#addmodal" title="Add a new transaction to the journal" >Add a transaction
+       <div .table-responsive>
+        ^{maincontent}
      |]
 
 postJournalR :: Handler Html
@@ -45,48 +48,41 @@
 -- | Render a "TransactionsReport" as html for the formatted journal view.
 journalTransactionsReportAsHtml :: WebOpts -> ViewData -> TransactionsReport -> HtmlUrl AppRoute
 journalTransactionsReportAsHtml _ vd (_,items) = [hamlet|
-<table.transactionsreport>
- <tr.headings>
-  <th.date style="text-align:left;">
+<table .transactionsreport .table .table-condensed>
+ <thead>
+  <th .date style="text-align:left;">
    Date
-   <span .glyphicon .glyphicon-chevron-up>
-  <th.description style="text-align:left;">Description
-  <th.account style="text-align:left;">Account
-  <th.amount style="text-align:right;">Amount
+  <th .description style="text-align:left;">Description
+  <th .account style="text-align:left;">Account
+  <th .amount style="text-align:right;">Amount
  $forall i <- numberTransactionsReportItems items
   ^{itemAsHtml vd i}
  |]
  where
 -- .#{datetransition}
    itemAsHtml :: ViewData -> (Int, Bool, Bool, Bool, TransactionsReportItem) -> HtmlUrl AppRoute
-   itemAsHtml VD{..} (n, _, _, _, (t, _, split, _, amt, _)) = [hamlet|
-<tr ##{date} .item.#{evenodd}.#{firstposting} style="vertical-align:top;" title="#{show t}">
- <td.date>#{date}
- <td.description colspan=2>#{elideRight 60 desc}
- <td.amount style="text-align:right;">
+   itemAsHtml VD{..} (_, _, _, _, (torig, _, split, _, amt, _)) = [hamlet|
+<tr .title #transaction-#{tindex torig}>
+ <td .date nowrap>#{date}
+ <td .description colspan=2>#{textElideRight 60 desc}
+ <td .amount style="text-align:right;">
   $if showamt
    \#{mixedAmountAsHtml amt}
-$forall p' <- tpostings t
- <tr .item.#{evenodd}.posting title="#{show t}">
-  <td.date>
-  <td.description>
-  <td.account>
+$forall p' <- tpostings torig
+ <tr .item .posting title="#{show torig}">
+  <td .nonhead>
+  <td .nonhead>
+  <td .nonhead>
    &nbsp;
-   <a href="@?{acctlink (paccount p')}##{date}" title="#{paccount p'}">#{elideAccountName 40 $ paccount p'}
-  <td.amount style="text-align:right;">#{mixedAmountAsHtml $ pamount p'}
-<tr.#{evenodd}>
- <td>&nbsp;
- <td>
- <td>
- <td>
+   <a href="@?{acctlink (paccount p')}##{tindex torig}" title="#{paccount p'}">#{elideAccountName 40 $ paccount p'}
+  <td .amount .nonhead style="text-align:right;">#{mixedAmountAsHtml $ pamount p'}
 |]
      where
-       acctlink a = (RegisterR, [("q", pack $ accountQuery a)])
-       evenodd = if even n then "even" else "odd" :: String
+       acctlink a = (RegisterR, [("q", T.pack $ accountQuery a)])
        -- datetransition | newm = "newmonth"
        --                | newd = "newday"
        --                | otherwise = "" :: String
-       (firstposting, date, desc) = (False, show $ tdate t, tdescription t)
-       -- acctquery = (here, [("q", pack $ accountQuery acct)])
+       (date, desc) = (show $ tdate torig, tdescription torig)
+       -- acctquery = (here, [("q", T.pack $ accountQuery acct)])
        showamt = not split || not (isZeroMixedAmount amt)
 
diff --git a/Handler/RegisterR.hs b/Handler/RegisterR.hs
--- a/Handler/RegisterR.hs
+++ b/Handler/RegisterR.hs
@@ -7,6 +7,8 @@
 
 import Data.List
 import Data.Maybe
+-- import Data.Text (Text)
+import qualified Data.Text as T
 import Safe
 
 import Handler.AddForm
@@ -28,14 +30,14 @@
   let -- injournal = isNothing inacct
       filtering = m /= Any
       -- title = "Transactions in "++a++s1++s2
-      title = a++s1++s2
+      title = T.unpack a++s1++s2
                where
                  (a,inclsubs) = fromMaybe ("all accounts",True) $ inAccount qopts
                  s1 = if inclsubs then "" else " (excluding subaccounts)"
                  s2 = if filtering then ", filtered" else ""
       maincontent = registerReportHtml opts vd $ accountTransactionsReport (reportopts_ $ cliopts_ opts) j m $ fromMaybe Any $ inAccountQuery qopts
   hledgerLayout vd "register" [hamlet|
-       <h2#contenttitle>#{title}
+       <h2 #contenttitle>#{title}
        <!-- p>Transactions affecting this account, with running balance. -->
        ^{maincontent}
      |]
@@ -46,24 +48,27 @@
 -- Generate html for an account register, including a balance chart and transaction list.
 registerReportHtml :: WebOpts -> ViewData -> TransactionsReport -> HtmlUrl AppRoute
 registerReportHtml opts vd r = [hamlet|
- ^{registerChartHtml $ transactionsReportByCommodity r}
+ <div .hidden-xs>
+  ^{registerChartHtml $ transactionsReportByCommodity r}
  ^{registerItemsHtml opts vd r}
 |]
 
 -- Generate html for a transaction list from an "TransactionsReport".
 registerItemsHtml :: WebOpts -> ViewData -> TransactionsReport -> HtmlUrl AppRoute
 registerItemsHtml _ vd (balancelabel,items) = [hamlet|
-<table.registerreport>
- <tr.headings>
-  <th.date style="text-align:left;">
-   Date
-   <span .glyphicon .glyphicon-chevron-up>
-  <th.description style="text-align:left;">Description
-  <th.account style="text-align:left;">To/From Account(s)
-  <th.amount style="text-align:right; white-space:normal;">Amount Out/In
-  <th.balance style="text-align:right; white-space:normal;">#{balancelabel'}
- $forall i <- numberTransactionsReportItems items
-  ^{itemAsHtml vd i}
+<div .table-responsive>
+ <table.registerreport .table .table-striped .table-condensed>
+  <thead>
+   <tr>
+    <th style="text-align:left;">
+     Date
+     <span .glyphicon .glyphicon-chevron-up>
+    <th style="text-align:left;">Description
+    <th style="text-align:left;">To/From Account(s)
+    <th style="text-align:right; white-space:normal;">Amount Out/In
+    <th style="text-align:right; white-space:normal;">#{balancelabel'}
+  $forall i <- numberTransactionsReportItems items
+   ^{itemAsHtml vd i}
  |]
  where
    insomeacct = isJust $ inAccount $ qopts vd
@@ -73,14 +78,15 @@
    itemAsHtml :: ViewData -> (Int, Bool, Bool, Bool, TransactionsReportItem) -> HtmlUrl AppRoute
    itemAsHtml VD{..} (n, newd, newm, _, (torig, tacct, split, acct, amt, bal)) = [hamlet|
 
-<tr ##{date} .item.#{evenodd}.#{firstposting}.#{datetransition} title="#{show torig}" style="vertical-align:top;">
- <td.date><a href="@{JournalR}##{date}">#{date}
- <td.description title="#{show torig}">#{elideRight 30 desc}
- <td.account>#{elideRight 40 acct}
- <td.amount style="text-align:right; white-space:nowrap;">
+<tr ##{tindex torig} .item.#{evenodd}.#{firstposting}.#{datetransition} title="#{show torig}" style="vertical-align:top;">
+ <td .date>
+  <a href="@{JournalR}#transaction-#{tindex torig}">#{date}
+ <td .description title="#{show torig}">#{textElideRight 30 desc}
+ <td .account>#{elideRight 40 acct}
+ <td .amount style="text-align:right; white-space:nowrap;">
   $if showamt
    \#{mixedAmountAsHtml amt}
- <td.balance style="text-align:right;">#{mixedAmountAsHtml bal}
+ <td .balance style="text-align:right;">#{mixedAmountAsHtml bal}
 |]
 
      where
@@ -98,13 +104,13 @@
                --                      Data.Foldable.Foldable t1 =>
                --                      t1 (Transaction, t2, t3, t4, t5, MixedAmount)
                --                      -> t -> Text.Blaze.Internal.HtmlM ()
-registerChartHtml :: [(Commodity, (String, [TransactionsReportItem]))] -> HtmlUrl AppRoute
+registerChartHtml :: [(CommoditySymbol, (String, [TransactionsReportItem]))] -> HtmlUrl AppRoute
 registerChartHtml percommoditytxnreports =
  -- have to make sure plot is not called when our container (maincontent)
  -- is hidden, eg with add form toggled
  [hamlet|
-<label#register-chart-label style=""><br>
-<div#register-chart style="width:85%; height:150px; margin-bottom:1em; display:block;">
+<label #register-chart-label style=""><br>
+<div #register-chart style="height:150px; margin-bottom:1em; display:block;">
 <script type=text/javascript>
  \$(document).ready(function() {
    var $chartdiv = $('#register-chart');
@@ -119,11 +125,11 @@
           $forall i <- reverse items
            [
             #{dayToJsTimestamp $ triDate i},
-            #{simpleMixedAmountQuantity $ triCommodityBalance c i},
+            #{simpleMixedAmountQuantity $ triCommodityBalance c i}
            ],
           /* [] */
         ],
-        label: '#{shownull c}',
+        label: '#{shownull $ T.unpack c}',
         color: #{colorForCommodity c},
         lines: {
           show: true,
@@ -145,6 +151,7 @@
             '#{showMixedAmountWithZeroCommodity $ triCommodityAmount c i}',
             '#{showMixedAmountWithZeroCommodity $ triCommodityBalance c i}',
             '#{concat $ intersperse "\\n" $ lines  $ show $ triOrigTransaction i}',
+            #{tindex $ triOrigTransaction i}
            ],
           /* [] */
         ],
@@ -169,6 +176,6 @@
            of "" -> ""
               s  -> s++":"
    colorForCommodity = fromMaybe 0 . flip lookup commoditiesIndex
-   commoditiesIndex = zip (map fst percommoditytxnreports) [0..] :: [(Commodity,Int)]
+   commoditiesIndex = zip (map fst percommoditytxnreports) [0..] :: [(CommoditySymbol,Int)]
    simpleMixedAmountQuantity = maybe 0 aquantity . headMay . amounts
    shownull c = if null c then " " else c
diff --git a/Hledger/Web/Main.hs b/Hledger/Web/Main.hs
--- a/Hledger/Web/Main.hs
+++ b/Hledger/Web/Main.hs
@@ -16,8 +16,8 @@
 import Settings            --  (parseExtra)
 import Application          (makeApplication)
 import Data.String
-import Network.Wai.Handler.Warp (runSettings, defaultSettings, setPort)
-import Network.Wai.Handler.Launch (runUrlPort)
+import Network.Wai.Handler.Warp (runSettings, defaultSettings, setHost, setPort)
+import Network.Wai.Handler.Launch (runHostPortUrl)
 --
 #if !MIN_VERSION_base(4,8,0)
 import Control.Applicative ((<$>))
@@ -35,16 +35,19 @@
 import Hledger.Web.WebOptions
 
 
-main :: IO ()
-main = do
+hledgerWebMain :: IO ()
+hledgerWebMain = do
   opts <- getHledgerWebOpts
   when (debug_ (cliopts_ opts) > 0) $ printf "%s\n" prognameandversion >> printf "opts: %s\n" (show opts)
   runWith opts
 
 runWith :: WebOpts -> IO ()
 runWith opts
-  | "help" `inRawOpts` (rawopts_ $ cliopts_ opts)            = putStr (showModeHelp webmode) >> exitSuccess
-  | "version" `inRawOpts` (rawopts_ $ cliopts_ opts)         = putStrLn prognameandversion >> exitSuccess
+  | "h"               `inRawOpts` (rawopts_ $ cliopts_ opts) = putStr (showModeUsage webmode) >> exitSuccess
+  | "help"            `inRawOpts` (rawopts_ $ cliopts_ opts) = printHelpForTopic (topicForMode webmode) >> exitSuccess
+  | "man"             `inRawOpts` (rawopts_ $ cliopts_ opts) = runManForTopic (topicForMode webmode) >> exitSuccess
+  | "info"            `inRawOpts` (rawopts_ $ cliopts_ opts) = runInfoForTopic (topicForMode webmode) >> exitSuccess
+  | "version"         `inRawOpts` (rawopts_ $ cliopts_ opts) = putStrLn prognameandversion >> exitSuccess
   | "binary-filename" `inRawOpts` (rawopts_ $ cliopts_ opts) = putStrLn (binaryfilename progname)
   | otherwise = do
     requireJournalFileExists =<< (head `fmap` journalFilePathFromOpts (cliopts_ opts)) -- XXX head should be safe for now
@@ -57,7 +60,7 @@
   -- https://github.com/simonmichael/hledger/issues/202
   -- -f- gives [Error#yesod-core] <stdin>: hGetContents: illegal operation (handle is closed) for some reason
   -- Also we may be writing to this file. Just disallow it.
-  when (f == "-") $ error' "hledger-web doesn't support --f -, please specify a file path"
+  when (f == "-") $ error' "hledger-web doesn't support -f -, please specify a file path"
 
   readJournalFile Nothing Nothing True f >>=
    either error' (cmd opts . journalApplyAliases (aliasesFromOpts $ cliopts_ opts))
@@ -66,24 +69,32 @@
 web :: WebOpts -> Journal -> IO ()
 web opts j = do
   d <- getCurrentDay
-  let j' = filterJournalTransactions (queryFromOpts d $ reportopts_ $ cliopts_ opts) j
+  let initq = queryFromOpts d $ reportopts_ $ cliopts_ opts
+      j' = filterJournalTransactions initq j
+      h = "127.0.0.1"
       p = port_ opts
       u = base_url_ opts
       staticRoot = pack <$> file_url_ opts
-  _ <- printf "Starting web app on port %d with base url %s\n" p u
-  app <- makeApplication opts j' AppConfig{appEnv = Development
-                                          ,appPort = p
-                                          ,appRoot = pack u
-                                          ,appHost = fromString "*4"
-                                          ,appExtra = Extra "" Nothing staticRoot
-                                          }
+      appconfig = AppConfig{appEnv = Development
+                           ,appHost = fromString h
+                           ,appPort = p
+                           ,appRoot = pack u
+                           ,appExtra = Extra "" Nothing staticRoot
+                           }
+  app <- makeApplication opts j' appconfig
+  _ <- printf "Starting web app on host %s port %d with base url %s\n" h p u
   if server_ opts
-   then do
-    putStrLn "Press ctrl-c to quit"
-    hFlush stdout
-    Network.Wai.Handler.Warp.runSettings (setPort p defaultSettings) app
-   else do
-    putStrLn "Starting web browser if possible"
-    putStrLn "Web app will auto-exit after a few minutes with no browsers (or press ctrl-c)"
-    hFlush stdout
-    Network.Wai.Handler.Launch.runUrlPort p "" app
+    then do
+      putStrLn "Press ctrl-c to quit"
+      hFlush stdout
+      let warpsettings =
+            setHost (fromString h) $
+            setPort p $
+            defaultSettings
+      Network.Wai.Handler.Warp.runSettings warpsettings app
+    else do
+      putStrLn "Starting web browser..."
+      putStrLn "Web app will auto-exit after a few minutes with no browsers (or press ctrl-c)"
+      hFlush stdout
+      Network.Wai.Handler.Launch.runHostPortUrl h p "" app
+
diff --git a/Hledger/Web/WebOptions.hs b/Hledger/Web/WebOptions.hs
--- a/Hledger/Web/WebOptions.hs
+++ b/Hledger/Web/WebOptions.hs
@@ -2,12 +2,11 @@
 module Hledger.Web.WebOptions
 where
 import Prelude
+import Data.Default
 #if !MIN_VERSION_base(4,8,0)
 import Data.Functor.Compat ((<$>))
 #endif
 import Data.Maybe
-import System.Console.CmdArgs
-import System.Console.CmdArgs.Explicit
 
 import Hledger.Cli hiding (progname,version,prognameandversion)
 import Settings
diff --git a/README b/README
new file mode 100644
--- /dev/null
+++ b/README
@@ -0,0 +1,1 @@
+A basic web UI for hledger data. Intended to be robust and somewhat useful.
diff --git a/app/main.hs b/app/main.hs
--- a/app/main.hs
+++ b/app/main.hs
@@ -1,9 +1,9 @@
 import Prelude              (IO)
 
-import qualified Hledger.Web.Main
+import Hledger.Web.Main
 
 main :: IO ()
-main = Hledger.Web.Main.main
+main = hledgerWebMain
 
 -- more standard yesod main, for reloading experiments
 
diff --git a/doc/hledger-web.1 b/doc/hledger-web.1
new file mode 100644
--- /dev/null
+++ b/doc/hledger-web.1
@@ -0,0 +1,319 @@
+
+.TH "hledger\-web" "1" "October 2016" "hledger\-web 1.0" "hledger User Manuals"
+
+
+
+.SH NAME
+.PP
+hledger\-web \- web interface for the hledger accounting tool
+.SH SYNOPSIS
+.PP
+\f[C]hledger\-web\ [OPTIONS]\f[]
+.PD 0
+.P
+.PD
+\f[C]hledger\ web\ \-\-\ [OPTIONS]\f[]
+.PP
+.PP
+.SH DESCRIPTION
+.PP
+hledger is a cross\-platform program for tracking money, time, or any
+other commodity, using double\-entry accounting and a simple, editable
+file format.
+hledger is inspired by and largely compatible with ledger(1).
+.PP
+hledger\-web is hledger\[aq]s web interface.
+It starts a simple web application for browsing and adding transactions,
+and optionally opens it in a web browser window if possible.
+It provides a more user\-friendly UI than the hledger CLI or hledger\-ui
+interface, showing more at once (accounts, the current account register,
+balance charts) and allowing history\-aware data entry, interactive
+searching, and bookmarking.
+.PP
+hledger\-web also lets you share a ledger with multiple users, or even
+the public web.
+There is no access control, so if you need that you should put it behind
+a suitable web proxy.
+As a small protection against data loss when running an unprotected
+instance, it writes a numbered backup of the main journal file (only ?)
+on every edit.
+.PP
+Like hledger, it reads data from one or more files in hledger journal,
+timeclock, timedot, or CSV format specified with \f[C]\-f\f[], or
+\f[C]$LEDGER_FILE\f[], or \f[C]$HOME/.hledger.journal\f[] (on windows,
+perhaps \f[C]C:/Users/USER/.hledger.journal\f[]).
+For more about this see hledger(1), hledger_journal(5) etc.
+.PP
+By default, hledger\-web starts the web app in "transient mode" and also
+opens it in your default web browser if possible.
+In this mode the web app will keep running for as long as you have it
+open in a browser window, and will exit after two minutes of inactivity
+(no requests and no browser windows viewing it).
+.IP
+.nf
+\f[C]
+$\ hledger\ web
+Starting\ web\ app\ on\ port\ 5000\ with\ base\ url\ http://localhost:5000
+Starting\ web\ browser\ if\ possible
+Web\ app\ will\ auto\-exit\ after\ a\ few\ minutes\ with\ no\ browsers\ (or\ press\ ctrl\-c)
+\f[]
+.fi
+.PP
+With \f[C]\-\-server\f[], it starts the web app in non\-transient mode
+and logs requests to the console.
+Typically when running hledger web as part of a website you\[aq]ll want
+to use \f[C]\-\-base\-url\f[] to set the protocol/hostname/port/path to
+be used in hyperlinks.
+The \f[C]\-\-file\-url\f[] option allows static files to be served from
+a different url, eg for better caching or cookie\-less serving.
+.PP
+You can use \f[C]\-\-port\f[] to listen on a different TCP port, eg if
+you are running multiple hledger\-web instances.
+This need not be the same as the PORT in the base url.
+.PP
+Note there is no built\-in access control, so you will need to hide
+hledger\-web behind an authenticating proxy (such as apache or nginx) if
+you want to restrict who can see and add entries to your journal.
+.PP
+Command\-line options and arguments may be used to set an initial filter
+on the data.
+This is not shown in the web UI, but it will be applied in addition to
+any search query entered there.
+.PP
+With journal and timeclock files (but not CSV files, currently) the web
+app detects changes made by other means and will show the new data on
+the next request.
+If a change makes the file unparseable, hledger\-web will show an error
+until the file has been fixed.
+.SH OPTIONS
+.PP
+Note: if invoking hledger\-web as a hledger subcommand, write
+\f[C]\-\-\f[] before options as shown above.
+.TP
+.B \f[C]\-\-server\f[]
+disable browser\-opening and auto\-exit\-on\-idle, and log all requests
+to stdout
+.RS
+.RE
+.TP
+.B \f[C]\-\-port=PORT\f[]
+set the TCP port to listen on (default: 5000)
+.RS
+.RE
+.TP
+.B \f[C]\-\-base\-url=URL\f[]
+set the base url (default: http://localhost:PORT).
+You would change this when sharing over the network, or integrating
+within a larger website.
+.RS
+.RE
+.TP
+.B \f[C]\-\-file\-url=URL\f[]
+set the static files url (default: BASEURL/static).
+hledger\-web normally serves static files itself, but if you wanted to
+serve them from another server for efficiency, you would set the url
+with this.
+.RS
+.RE
+.PP
+hledger general options:
+.TP
+.B \f[C]\-h\f[]
+show general usage (or after COMMAND, the command\[aq]s usage)
+.RS
+.RE
+.TP
+.B \f[C]\-\-help\f[]
+show the current program\[aq]s manual as plain text (or after an add\-on
+COMMAND, the add\-on\[aq]s manual)
+.RS
+.RE
+.TP
+.B \f[C]\-\-man\f[]
+show the current program\[aq]s manual with man
+.RS
+.RE
+.TP
+.B \f[C]\-\-info\f[]
+show the current program\[aq]s manual with info
+.RS
+.RE
+.TP
+.B \f[C]\-\-version\f[]
+show version
+.RS
+.RE
+.TP
+.B \f[C]\-\-debug[=N]\f[]
+show debug output (levels 1\-9, default: 1)
+.RS
+.RE
+.TP
+.B \f[C]\-f\ FILE\ \-\-file=FILE\f[]
+use a different input file.
+For stdin, use \-
+.RS
+.RE
+.TP
+.B \f[C]\-\-rules\-file=RULESFILE\f[]
+Conversion rules file to use when reading CSV (default: FILE.rules)
+.RS
+.RE
+.TP
+.B \f[C]\-\-alias=OLD=NEW\f[]
+display accounts named OLD as NEW
+.RS
+.RE
+.TP
+.B \f[C]\-I\ \-\-ignore\-assertions\f[]
+ignore any failing balance assertions in the journal
+.RS
+.RE
+.PP
+hledger reporting options:
+.TP
+.B \f[C]\-b\ \-\-begin=DATE\f[]
+include postings/txns on or after this date
+.RS
+.RE
+.TP
+.B \f[C]\-e\ \-\-end=DATE\f[]
+include postings/txns before this date
+.RS
+.RE
+.TP
+.B \f[C]\-D\ \-\-daily\f[]
+multiperiod/multicolumn report by day
+.RS
+.RE
+.TP
+.B \f[C]\-W\ \-\-weekly\f[]
+multiperiod/multicolumn report by week
+.RS
+.RE
+.TP
+.B \f[C]\-M\ \-\-monthly\f[]
+multiperiod/multicolumn report by month
+.RS
+.RE
+.TP
+.B \f[C]\-Q\ \-\-quarterly\f[]
+multiperiod/multicolumn report by quarter
+.RS
+.RE
+.TP
+.B \f[C]\-Y\ \-\-yearly\f[]
+multiperiod/multicolumn report by year
+.RS
+.RE
+.TP
+.B \f[C]\-p\ \-\-period=PERIODEXP\f[]
+set start date, end date, and/or reporting interval all at once
+(overrides the flags above)
+.RS
+.RE
+.TP
+.B \f[C]\-\-date2\f[]
+show, and match with \-b/\-e/\-p/date:, secondary dates instead
+.RS
+.RE
+.TP
+.B \f[C]\-C\ \-\-cleared\f[]
+include only cleared postings/txns
+.RS
+.RE
+.TP
+.B \f[C]\-\-pending\f[]
+include only pending postings/txns
+.RS
+.RE
+.TP
+.B \f[C]\-U\ \-\-uncleared\f[]
+include only uncleared (and pending) postings/txns
+.RS
+.RE
+.TP
+.B \f[C]\-R\ \-\-real\f[]
+include only non\-virtual postings
+.RS
+.RE
+.TP
+.B \f[C]\-\-depth=N\f[]
+hide accounts/postings deeper than N
+.RS
+.RE
+.TP
+.B \f[C]\-E\ \-\-empty\f[]
+show items with zero amount, normally hidden
+.RS
+.RE
+.TP
+.B \f[C]\-B\ \-\-cost\f[]
+show amounts in their cost price\[aq]s commodity
+.RS
+.RE
+.TP
+.B \f[C]\-\-pivot\ TAG\f[]
+will transform the journal before any other processing by replacing the
+account name of every posting having the tag TAG with content VALUE by
+the account name "TAG:VALUE".
+.RS
+.RE
+The TAG will only match if it is a full\-length match.
+The pivot will only happen if the TAG is on a posting, not if it is on
+the transaction.
+If the tag value is a multi:level:account:name the new account name will
+be "TAG:multi:level:account:name".
+.RS
+.RE
+.TP
+.B \f[C]\-\-anon\f[]
+show anonymized accounts and payees
+.RS
+.RE
+.SH ENVIRONMENT
+.PP
+\f[B]LEDGER_FILE\f[] The journal file path when not specified with
+\f[C]\-f\f[].
+Default: \f[C]~/.hledger.journal\f[] (on windows, perhaps
+\f[C]C:/Users/USER/.hledger.journal\f[]).
+.SH FILES
+.PP
+Reads data from one or more files in hledger journal, timeclock,
+timedot, or CSV format specified with \f[C]\-f\f[], or
+\f[C]$LEDGER_FILE\f[], or \f[C]$HOME/.hledger.journal\f[] (on windows,
+perhaps \f[C]C:/Users/USER/.hledger.journal\f[]).
+.SH BUGS
+.PP
+The need to precede options with \f[C]\-\-\f[] when invoked from hledger
+is awkward.
+.PP
+\f[C]\-f\-\f[] doesn\[aq]t work (hledger\-web can\[aq]t read from
+stdin).
+.PP
+Query arguments and some hledger options are ignored.
+.PP
+Does not work in text\-mode browsers.
+.PP
+Does not work well on small screens.
+
+
+.SH "REPORTING BUGS"
+Report bugs at http://bugs.hledger.org
+(or on the #hledger IRC channel or hledger mail list)
+
+.SH AUTHORS
+Simon Michael <simon@joyful.com> and contributors
+
+.SH COPYRIGHT
+
+Copyright (C) 2007-2016 Simon Michael.
+.br
+Released under GNU GPL v3 or later.
+
+.SH SEE ALSO
+hledger(1), hledger\-ui(1), hledger\-web(1), hledger\-api(1),
+hledger_csv(5), hledger_journal(5), hledger_timeclock(5), hledger_timedot(5),
+ledger(1)
+
+http://hledger.org
diff --git a/doc/hledger-web.1.info b/doc/hledger-web.1.info
new file mode 100644
--- /dev/null
+++ b/doc/hledger-web.1.info
@@ -0,0 +1,199 @@
+This is hledger-web/doc/hledger-web.1.info, produced by makeinfo
+version 4.8 from stdin.
+
+
+File: hledger-web.1.info,  Node: Top,  Up: (dir)
+
+hledger-web(1) hledger-web 1.0
+******************************
+
+hledger-web is hledger's web interface. It starts a simple web
+application for browsing and adding transactions, and optionally opens
+it in a web browser window if possible. It provides a more user-friendly
+UI than the hledger CLI or hledger-ui interface, showing more at once
+(accounts, the current account register, balance charts) and allowing
+history-aware data entry, interactive searching, and bookmarking.
+
+   hledger-web also lets you share a ledger with multiple users, or even
+the public web. There is no access control, so if you need that you
+should put it behind a suitable web proxy. As a small protection against
+data loss when running an unprotected instance, it writes a numbered
+backup of the main journal file (only ?) on every edit.
+
+   Like hledger, it reads data from one or more files in hledger
+journal, timeclock, timedot, or CSV format specified with `-f', or
+`$LEDGER_FILE', or `$HOME/.hledger.journal' (on windows, perhaps
+`C:/Users/USER/.hledger.journal'). For more about this see hledger(1),
+hledger_journal(5) etc.
+
+   By default, hledger-web starts the web app in "transient mode" and
+also opens it in your default web browser if possible. In this mode the
+web app will keep running for as long as you have it open in a browser
+window, and will exit after two minutes of inactivity (no requests and
+no browser windows viewing it).
+
+
+$ hledger web
+Starting web app on port 5000 with base url http://localhost:5000
+Starting web browser if possible
+Web app will auto-exit after a few minutes with no browsers (or press ctrl-c)
+
+   With `--server', it starts the web app in non-transient mode and
+logs requests to the console. Typically when running hledger web as part
+of a website you'll want to use `--base-url' to set the
+protocol/hostname/port/path to be used in hyperlinks. The `--file-url'
+option allows static files to be served from a different url, eg for
+better caching or cookie-less serving.
+
+   You can use `--port' to listen on a different TCP port, eg if you
+are running multiple hledger-web instances. This need not be the same as
+the PORT in the base url.
+
+   Note there is no built-in access control, so you will need to hide
+hledger-web behind an authenticating proxy (such as apache or nginx) if
+you want to restrict who can see and add entries to your journal.
+
+   Command-line options and arguments may be used to set an initial
+filter on the data. This is not shown in the web UI, but it will be
+applied in addition to any search query entered there.
+
+   With journal and timeclock files (but not CSV files, currently) the
+web app detects changes made by other means and will show the new data
+on the next request. If a change makes the file unparseable, hledger-web
+will show an error until the file has been fixed.
+
+* Menu:
+
+* OPTIONS::
+
+
+File: hledger-web.1.info,  Node: OPTIONS,  Prev: Top,  Up: Top
+
+1 OPTIONS
+*********
+
+Note: if invoking hledger-web as a hledger subcommand, write `--'
+before options as shown above.
+
+`--server'
+     disable browser-opening and auto-exit-on-idle, and log all
+     requests to stdout
+
+`--port=PORT'
+     set the TCP port to listen on (default: 5000)
+
+`--base-url=URL'
+     set the base url (default: http://localhost:PORT). You would
+     change this when sharing over the network, or integrating within a
+     larger website.
+
+`--file-url=URL'
+     set the static files url (default: BASEURL/static). hledger-web
+     normally serves static files itself, but if you wanted to serve
+     them from another server for efficiency, you would set the url
+     with this.
+
+   hledger general options:
+
+`-h'
+     show general usage (or after COMMAND, the command's usage)
+
+`--help'
+     show the current program's manual as plain text (or after an add-on
+     COMMAND, the add-on's manual)
+
+`--man'
+     show the current program's manual with man
+
+`--info'
+     show the current program's manual with info
+
+`--version'
+     show version
+
+`--debug[=N]'
+     show debug output (levels 1-9, default: 1)
+
+`-f FILE --file=FILE'
+     use a different input file. For stdin, use -
+
+`--rules-file=RULESFILE'
+     Conversion rules file to use when reading CSV (default: FILE.rules)
+
+`--alias=OLD=NEW'
+     display accounts named OLD as NEW
+
+`-I --ignore-assertions'
+     ignore any failing balance assertions in the journal
+
+   hledger reporting options:
+
+`-b --begin=DATE'
+     include postings/txns on or after this date
+
+`-e --end=DATE'
+     include postings/txns before this date
+
+`-D --daily'
+     multiperiod/multicolumn report by day
+
+`-W --weekly'
+     multiperiod/multicolumn report by week
+
+`-M --monthly'
+     multiperiod/multicolumn report by month
+
+`-Q --quarterly'
+     multiperiod/multicolumn report by quarter
+
+`-Y --yearly'
+     multiperiod/multicolumn report by year
+
+`-p --period=PERIODEXP'
+     set start date, end date, and/or reporting interval all at once
+     (overrides the flags above)
+
+`--date2'
+     show, and match with -b/-e/-p/date:, secondary dates instead
+
+`-C --cleared'
+     include only cleared postings/txns
+
+`--pending'
+     include only pending postings/txns
+
+`-U --uncleared'
+     include only uncleared (and pending) postings/txns
+
+`-R --real'
+     include only non-virtual postings
+
+`--depth=N'
+     hide accounts/postings deeper than N
+
+`-E --empty'
+     show items with zero amount, normally hidden
+
+`-B --cost'
+     show amounts in their cost price's commodity
+
+`--pivot TAG'
+     will transform the journal before any other processing by
+     replacing the account name of every posting having the tag TAG
+     with content VALUE by the account name "TAG:VALUE".  The TAG will
+     only match if it is a full-length match. The pivot will only
+     happen if the TAG is on a posting, not if it is on the transaction.
+     If the tag value is a multi:level:account:name the new account
+     name will be "TAG:multi:level:account:name".
+
+`--anon'
+     show anonymized accounts and payees
+
+
+
+Tag Table:
+Node: Top90
+Node: OPTIONS2997
+Ref: #options3084
+
+End Tag Table
diff --git a/doc/hledger-web.1.txt b/doc/hledger-web.1.txt
new file mode 100644
--- /dev/null
+++ b/doc/hledger-web.1.txt
@@ -0,0 +1,237 @@
+
+hledger-web(1)               hledger User Manuals               hledger-web(1)
+
+
+
+NAME
+       hledger-web - web interface for the hledger accounting tool
+
+SYNOPSIS
+       hledger-web [OPTIONS]
+       hledger web -- [OPTIONS]
+
+
+
+DESCRIPTION
+       hledger  is  a  cross-platform program for tracking money, time, or any
+       other commodity, using double-entry accounting and a  simple,  editable
+       file  format.   hledger  is  inspired  by  and  largely compatible with
+       ledger(1).
+
+       hledger-web is hledger's web interface.  It starts a simple web  appli-
+       cation for browsing and adding transactions, and optionally opens it in
+       a web browser window if possible.  It provides a more user-friendly  UI
+       than  the  hledger  CLI  or  hledger-ui interface, showing more at once
+       (accounts, the current account register, balance charts)  and  allowing
+       history-aware data entry, interactive searching, and bookmarking.
+
+       hledger-web  also  lets you share a ledger with multiple users, or even
+       the public web.  There is no access control, so if you  need  that  you
+       should  put  it  behind  a  suitable  web proxy.  As a small protection
+       against data loss when running an unprotected  instance,  it  writes  a
+       numbered backup of the main journal file (only ?)  on every edit.
+
+       Like  hledger, it reads data from one or more files in hledger journal,
+       timeclock, timedot, or CSV format specified with -f,  or  $LEDGER_FILE,
+       or        $HOME/.hledger.journal       (on       windows,       perhaps
+       C:/Users/USER/.hledger.journal).  For more about this  see  hledger(1),
+       hledger_journal(5) etc.
+
+       By default, hledger-web starts the web app in "transient mode" and also
+       opens it in your default web browser if possible.  In this mode the web
+       app will keep running for as long as you have it open in a browser win-
+       dow, and will exit after two minutes of inactivity (no requests and  no
+       browser windows viewing it).
+
+              $ hledger web
+              Starting web app on port 5000 with base url http://localhost:5000
+              Starting web browser if possible
+              Web app will auto-exit after a few minutes with no browsers (or press ctrl-c)
+
+       With  --server,  it  starts  the web app in non-transient mode and logs
+       requests to the console.  Typically when running hledger web as part of
+       a  website  you'll  want  to  use  --base-url to set the protocol/host-
+       name/port/path to be used in hyperlinks.  The --file-url option  allows
+       static  files  to be served from a different url, eg for better caching
+       or cookie-less serving.
+
+       You can use --port to listen on a different TCP port,  eg  if  you  are
+       running  multiple  hledger-web instances.  This need not be the same as
+       the PORT in the base url.
+
+       Note there is no built-in access control, so  you  will  need  to  hide
+       hledger-web behind an authenticating proxy (such as apache or nginx) if
+       you want to restrict who can see and add entries to your journal.
+
+       Command-line options and arguments may be used to set an initial filter
+       on  the  data.  This is not shown in the web UI, but it will be applied
+       in addition to any search query entered there.
+
+       With journal and timeclock files (but not CSV files, currently) the web
+       app  detects  changes made by other means and will show the new data on
+       the next request.  If a change makes the file unparseable,  hledger-web
+       will show an error until the file has been fixed.
+
+OPTIONS
+       Note:  if invoking hledger-web as a hledger subcommand, write -- before
+       options as shown above.
+
+       --server
+              disable  browser-opening  and  auto-exit-on-idle,  and  log  all
+              requests to stdout
+
+       --port=PORT
+              set the TCP port to listen on (default: 5000)
+
+       --base-url=URL
+              set  the  base  url (default: http://localhost:PORT).  You would
+              change this when sharing over the network, or integrating within
+              a larger website.
+
+       --file-url=URL
+              set the static files url (default: BASEURL/static).  hledger-web
+              normally serves static files itself, but if you wanted to  serve
+              them  from  another server for efficiency, you would set the url
+              with this.
+
+       hledger general options:
+
+       -h     show general usage (or after COMMAND, the command's usage)
+
+       --help show the current program's manual as plain  text  (or  after  an
+              add-on COMMAND, the add-on's manual)
+
+       --man  show the current program's manual with man
+
+       --info show the current program's manual with info
+
+       --version
+              show version
+
+       --debug[=N]
+              show debug output (levels 1-9, default: 1)
+
+       -f FILE --file=FILE
+              use a different input file.  For stdin, use -
+
+       --rules-file=RULESFILE
+              Conversion   rules  file  to  use  when  reading  CSV  (default:
+              FILE.rules)
+
+       --alias=OLD=NEW
+              display accounts named OLD as NEW
+
+       -I --ignore-assertions
+              ignore any failing balance assertions in the journal
+
+       hledger reporting options:
+
+       -b --begin=DATE
+              include postings/txns on or after this date
+
+       -e --end=DATE
+              include postings/txns before this date
+
+       -D --daily
+              multiperiod/multicolumn report by day
+
+       -W --weekly
+              multiperiod/multicolumn report by week
+
+       -M --monthly
+              multiperiod/multicolumn report by month
+
+       -Q --quarterly
+              multiperiod/multicolumn report by quarter
+
+       -Y --yearly
+              multiperiod/multicolumn report by year
+
+       -p --period=PERIODEXP
+              set start date, end date, and/or reporting interval all at  once
+              (overrides the flags above)
+
+       --date2
+              show, and match with -b/-e/-p/date:, secondary dates instead
+
+       -C --cleared
+              include only cleared postings/txns
+
+       --pending
+              include only pending postings/txns
+
+       -U --uncleared
+              include only uncleared (and pending) postings/txns
+
+       -R --real
+              include only non-virtual postings
+
+       --depth=N
+              hide accounts/postings deeper than N
+
+       -E --empty
+              show items with zero amount, normally hidden
+
+       -B --cost
+              show amounts in their cost price's commodity
+
+       --pivot TAG
+              will  transform  the  journal  before  any  other  processing by
+              replacing the account name of every posting having the  tag  TAG
+              with content VALUE by the account name "TAG:VALUE".
+       The  TAG  will only match if it is a full-length match.  The pivot will
+       only happen if the TAG is on a posting, not if it is  on  the  transac-
+       tion.   If  the tag value is a multi:level:account:name the new account
+       name will be "TAG:multi:level:account:name".
+
+       --anon show anonymized accounts and payees
+
+ENVIRONMENT
+       LEDGER_FILE The journal file path when not specified with -f.  Default:
+       ~/.hledger.journal  (on  windows,  perhaps C:/Users/USER/.hledger.jour-
+       nal).
+
+FILES
+       Reads data from one or more files in hledger journal, timeclock,  time-
+       dot,   or   CSV   format   specified   with  -f,  or  $LEDGER_FILE,  or
+       $HOME/.hledger.journal          (on          windows,           perhaps
+       C:/Users/USER/.hledger.journal).
+
+BUGS
+       The  need  to precede options with -- when invoked from hledger is awk-
+       ward.
+
+       -f- doesn't work (hledger-web can't read from stdin).
+
+       Query arguments and some hledger options are ignored.
+
+       Does not work in text-mode browsers.
+
+       Does not work well on small screens.
+
+
+
+REPORTING BUGS
+       Report bugs at http://bugs.hledger.org (or on the #hledger IRC  channel
+       or hledger mail list)
+
+
+AUTHORS
+       Simon Michael <simon@joyful.com> and contributors
+
+
+COPYRIGHT
+       Copyright (C) 2007-2016 Simon Michael.
+       Released under GNU GPL v3 or later.
+
+
+SEE ALSO
+       hledger(1),      hledger-ui(1),     hledger-web(1),     hledger-api(1),
+       hledger_csv(5), hledger_journal(5), hledger_timeclock(5), hledger_time-
+       dot(5), ledger(1)
+
+       http://hledger.org
+
+
+
+hledger-web 1.0                  October 2016                   hledger-web(1)
diff --git a/hledger-web.cabal b/hledger-web.cabal
--- a/hledger-web.cabal
+++ b/hledger-web.cabal
@@ -1,22 +1,22 @@
--- This file has been generated from package.yaml by hpack version 0.5.4.
+-- This file has been generated from package.yaml by hpack version 0.14.0.
 --
 -- see: https://github.com/sol/hpack
 
 name:           hledger-web
-version:        0.27
+version:        1.0
 stability:      stable
 category:       Finance
 synopsis:       Web interface for the hledger accounting tool
-description:
-    This is hledger's web interface.
-    It provides a more user-friendly and collaborative UI than the
-    command-line or curses-style interfaces.
-    hledger is a cross-platform program for tracking money, time, or
-    any other commodity, using double-entry accounting and a simple,
-    editable file format. It is inspired by and largely compatible
-    with ledger(1).  hledger provides command-line, curses and web
-    interfaces, and aims to be a reliable, practical tool for daily
-    use.
+description:    This is hledger's web interface.
+                It provides a more user-friendly and collaborative UI than the
+                command-line or curses-style interfaces.
+                .
+                hledger is a cross-platform program for tracking money, time, or
+                any other commodity, using double-entry accounting and a simple,
+                editable file format. It is inspired by and largely compatible
+                with ledger(1).  hledger provides command-line, curses and web
+                interfaces, and aims to be a reliable, practical tool for daily
+                use.
 license:        GPL
 license-file:   LICENSE
 author:         Simon Michael <simon@joyful.com>
@@ -25,7 +25,7 @@
 bug-reports:    http://bugs.hledger.org
 cabal-version:  >= 1.10
 build-type:     Simple
-tested-with:    GHC==7.6.3, GHC==7.8.4, GHC==7.10.2
+tested-with:    GHC==7.10.3, GHC==8.0
 
 extra-source-files:
       CHANGES
@@ -35,6 +35,7 @@
       config/routes
       config/settings.yml
       messages/en.msg
+      README
       static/css/bootstrap-theme.css
       static/css/bootstrap-theme.css.map
       static/css/bootstrap-theme.min.css
@@ -92,52 +93,49 @@
       static/js/typeahead.bundle.min.js
       templates/default-layout-wrapper.hamlet
       templates/default-layout.hamlet
-      templates/homepage.hamlet
-      templates/homepage.julius
-      templates/homepage.lucius
-      templates/normalize.lucius
 
+data-files:
+      doc/hledger-web.1
+      doc/hledger-web.1.info
+      doc/hledger-web.1.txt
+
 source-repository head
     type: git
     location: https://github.com/simonmichael/hledger
 
-flag threaded
-  default: True
-  description:
-    Build with support for multithreaded execution.
-
 flag dev
-  default: False
-  description:
-    Turn on development settings, like auto-reload templates.
+    default: False
+    description: Turn on development settings, like auto-reload templates.
+    manual: False
 
 flag library-only
-  default: False
-  description:
-    Build for use with "yesod devel"
+    default: False
+    description: Build for use with "yesod devel"
+    manual: False
 
-flag old-locale
-  default: False
-  description:
-    A compatibility flag, set automatically by cabal.
-    If false then depend on time >= 1.5, 
-    if true then depend on time < 1.5 together with old-locale.
+flag oldtime
+    description: If building with time < 1.5, also depend on old-locale. Set automatically by cabal.
+    manual: False
+    default: False
 
+flag threaded
+    default: True
+    description: Build with support for multithreaded execution.
+    manual: False
+
 library
     ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans
-    cpp-options: -DVERSION="0.27"
-    if flag(dev) || flag(library-only)
-        cpp-options: -DDEVELOPMENT
+    cpp-options: -DVERSION="1.0"
     build-depends:
-          hledger-lib == 0.27
-        , hledger == 0.27
-        , base               >= 4 && < 5
-        , base-compat        >= 0.8.1
+          hledger-lib >= 1.0 && < 1.1
+        , hledger >= 1.0 && < 1.1
+        , base >=4 && <5
+        , base-compat >=0.8.1
         , blaze-html
         , blaze-markup
         , bytestring
         , clientsession
-        , cmdargs            >= 0.10 && < 0.11
+        , cmdargs >=0.10 && <0.11
         , data-default
         , directory
         , filepath
@@ -145,29 +143,36 @@
         , http-conduit
         , http-client
         , HUnit
-        , conduit-extra      >= 1.1
-        , parsec             >= 3
-        , safe               >= 0.2
-        , shakespeare        >= 2.0
+        , conduit-extra >=1.1
+        , safe >=0.2
+        , shakespeare >=2.0
         , template-haskell
-        , text
+        , text >=1.2 && <1.3
         , transformers
         , wai
         , wai-extra
-        , wai-handler-launch >= 1.3
+        , wai-handler-launch >=1.3
         , warp
         , yaml
-        , yesod              >= 1.4 && < 1.5
+        , yesod >=1.4 && <1.5
         , yesod-core
         , yesod-form
         , yesod-static
         , json
-
-    if flag(old-locale)
-      build-depends: time < 1.5, old-locale
+        , megaparsec >=5 && < 5.1
+        , mtl
+    if (flag(dev)) || (flag(library-only))
+        cpp-options: -DDEVELOPMENT
+    if flag(oldtime)
+        build-depends:
+              time <1.5
+            , old-locale
     else
-      build-depends: time >= 1.5
-
+        build-depends:
+              time >=1.5
+    if impl(ghc <7.6)
+        build-depends:
+              ghc-prim
     exposed-modules:
           Application
           Foundation
@@ -185,32 +190,26 @@
           Settings
           Settings.Development
           Settings.StaticFiles
+    other-modules:
+          Paths_hledger_web
     default-language: Haskell2010
 
 executable hledger-web
-    if flag(library-only)
-      buildable: False
     main-is: main.hs
     hs-source-dirs:
           app
     ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans
-    if flag(threaded)
-      ghc-options: -threaded
-    if flag(dev)
-      ghc-options:   -O0
-    cpp-options: -DVERSION="0.27"
-    if flag(dev)
-      cpp-options: -DDEVELOPMENT
+    cpp-options: -DVERSION="1.0"
     build-depends:
-          hledger-lib == 0.27
-        , hledger == 0.27
-        , base               >= 4 && < 5
-        , base-compat        >= 0.8.1
+          hledger-lib >= 1.0 && < 1.1
+        , hledger >= 1.0 && < 1.1
+        , base >=4 && <5
+        , base-compat >=0.8.1
         , blaze-html
         , blaze-markup
         , bytestring
         , clientsession
-        , cmdargs            >= 0.10 && < 0.11
+        , cmdargs >=0.10 && <0.11
         , data-default
         , directory
         , filepath
@@ -218,30 +217,42 @@
         , http-conduit
         , http-client
         , HUnit
-        , conduit-extra      >= 1.1
-        , parsec             >= 3
-        , safe               >= 0.2
-        , shakespeare        >= 2.0
+        , conduit-extra >=1.1
+        , safe >=0.2
+        , shakespeare >=2.0
         , template-haskell
-        , text
+        , text >=1.2 && <1.3
         , transformers
         , wai
         , wai-extra
-        , wai-handler-launch >= 1.3
+        , wai-handler-launch >=1.3
         , warp
         , yaml
-        , yesod              >= 1.4 && < 1.5
+        , yesod >=1.4 && <1.5
         , yesod-core
         , yesod-form
         , yesod-static
         , json
-        , hledger-web == 0.27
-
-    if flag(old-locale)
-      build-depends: time < 1.5, old-locale
+        , parsec >=3
+        , hledger-web == 1.0
+    if flag(library-only)
+        buildable: False
+    if flag(threaded)
+        ghc-options: -threaded
+    if flag(dev)
+        ghc-options: -O0
+    if flag(dev)
+        cpp-options: -DDEVELOPMENT
+    if flag(oldtime)
+        build-depends:
+              time <1.5
+            , old-locale
     else
-      build-depends: time >= 1.5
-
+        build-depends:
+              time >=1.5
+    if impl(ghc <7.6)
+        build-depends:
+              ghc-prim
     default-language: Haskell2010
 
 test-suite test
@@ -253,17 +264,17 @@
     hs-source-dirs:
           tests
     ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans
-    cpp-options: -DVERSION="0.27"
+    cpp-options: -DVERSION="1.0"
     build-depends:
-          hledger-lib == 0.27
-        , hledger == 0.27
-        , base               >= 4 && < 5
-        , base-compat        >= 0.8.1
+          hledger-lib >= 1.0 && < 1.1
+        , hledger >= 1.0 && < 1.1
+        , base >=4 && <5
+        , base-compat >=0.8.1
         , blaze-html
         , blaze-markup
         , bytestring
         , clientsession
-        , cmdargs            >= 0.10 && < 0.11
+        , cmdargs >=0.10 && <0.11
         , data-default
         , directory
         , filepath
@@ -271,19 +282,18 @@
         , http-conduit
         , http-client
         , HUnit
-        , conduit-extra      >= 1.1
-        , parsec             >= 3
-        , safe               >= 0.2
-        , shakespeare        >= 2.0
+        , conduit-extra >=1.1
+        , safe >=0.2
+        , shakespeare >=2.0
         , template-haskell
-        , text
+        , text >=1.2 && <1.3
         , transformers
         , wai
         , wai-extra
-        , wai-handler-launch >= 1.3
+        , wai-handler-launch >=1.3
         , warp
         , yaml
-        , yesod              >= 1.4 && < 1.5
+        , yesod >=1.4 && <1.5
         , yesod-core
         , yesod-form
         , yesod-static
@@ -291,10 +301,14 @@
         , hledger-web
         , hspec
         , yesod-test
-
-    if flag(old-locale)
-      build-depends: time < 1.5, old-locale
+    if flag(oldtime)
+        build-depends:
+              time <1.5
+            , old-locale
     else
-      build-depends: time >= 1.5
-
+        build-depends:
+              time >=1.5
+    if impl(ghc <7.6)
+        build-depends:
+              ghc-prim
     default-language: Haskell2010
diff --git a/static/hledger.css b/static/hledger.css
--- a/static/hledger.css
+++ b/static/hledger.css
@@ -4,174 +4,6 @@
 /* 1. colours */
 
 /* green */
-body                                                                 { background-color:white; color:black; }
-.registerreport .odd                                                 { background-color:#ded; }
-/* .transactionsreport .odd                                             { background-color:#eee; } */
-.filtering                                                           { background-color:#e0e0e0; }
-a:link, a:visited { color:#00e; }
-/* a:link:hover, a:visited:hover { color:red; } */
-/* #main                                                                { border-color:#e0e0e0; } see below */
-/* .journalreport td                                                    { border-color:thin solid #e0e0e0; } see below */
-
-/* white */
-/* body                                                                 { background-color:#fff; } */
-/* .registerreport .odd                                                 { background-color:#eee; } */
-/* .filtering                                                           { background-color:#ddd; } */
-/* #main                                                                { border-color:#eee; } see below */
-/* .journalreport td                                                    { border-color:thin solid #eee; } see below */
-
-#message                                                             { color:red; background-color:#fee; }
-/* #addform input.textinput, #addform .dhx_combo_input, .dhx_combo_list { /\*background-color:#eee;*\/ } */
-#editform textarea                                                   { background-color:#eee; }
-.negative                                                            { color:#800; }
-.help                                                                {  }
-
-#sidebar .hoverlinks { visibility:hidden; }
-/* #sidebar .mouseover { background-color:rgba(208,208,208,0.5); } */
-#sidebar .mouseover .hoverlinks { visibility:visible; }
-
-#sidebar .balancereport .hoverlinks { margin-left:0em; font-weight:normal; /*font-size:smaller;*/ display:inline-block; text-align:right; }
-#sidebar .balancereport .hoverlinks a { margin-left:0.5em; }
-/* #sidebar .notinacct, .notinacct :link, .notinacct :visited { color:#888; } */
-#sidebar .notinacct .negative { color:#b77; }
-#sidebar .balancereport .inacct { font-weight:bold; }
-/* #sidebar .balancereport .inacct { background-color:#e0e0e0; } */
-#sidebar .balancereport .numpostings                                          { padding-left:1em; color:#aaa; }
-#sidebar .current { font-weight:bold; }
-
-/*------------------------------------------------------------------------------------------*/
-/* 2. font families & sizes */
-/* overspecified for cross-browser robustness */
-body { font-size:16px; }
-/*
-body                                               { font-family:helvetica,arial,sans-serif; }
-pre                                                { font-family:courier,"courier new",monospace; }
-.dhx_combo_input, .dhx_combo_list { font-size:small; }
-#editform textarea                                 { font-family:courier,"courier new",monospace; font-size:small; }
-.nav2                                              { font-size:small; }
-#searchform                                        { font-size:small; }
-.topleftlink                                       { font-size:small; }
-.toprightlink                                      { font-size:small; }
-#journaldesc                                       { font-size:small; }
-.togglelink                                        { font-size:smaller; white-space:nowrap }
-.help                                              { font-size:smaller; }
-.form                                              { font-size:small; }
-.transactionsreport                                { font-size:small; }
-.entriesreport                                     { font-size:small; }
-.balancereport                                     { font-size:small; }
-.registerreport                                    { font-size:small; }
-.showall                                           { font-size:small; }
-*/
-/* #addformlink                                    { font-size:small; } */
-/* #editformlink                                   { font-size:small; } */
-/*
-#contenttitle                                    { font-size:1.2em; }
-*/
-
-/*------------------------------------------------------------------------------------------*/
-/* 3. layout */
-
-body                        { margin:0; }
-
-
-#topbar                     { padding:2px; }
-.topleftlink                { float:left; margin-right:1em; padding:2px; }
-.toprightlink               { float:right; margin-left:1em; padding:2px; }
-#topbar h1                  { display:inline-block; vertical-align:top; margin:0; }
-#journalinfo                { vertical-align:middle; margin:0; }
-/* #topbar                  { padding:4px; border-bottom:2px solid #ddd; } */
-
-#message                    { margin:0.5em;}
-.help                       { font-style: italic; }
-.helprow td                 { padding-bottom:8px; }
-
-#outermain { overflow:auto; }
-#main { overflow:auto; padding-left:1em; }
-
-#sidebar {
-  float:left;
-  padding-right:1em; 
-  border-right:thin solid #e0e0e0;
-  margin-bottom:5em;
-}
-/* #sidebar.affix { */
-/*     position: fixed; */
-/*     top: 20px; */
-/* } */
-
-.balancereport .item { border-top:thin solid #e0e0e0; }
-
-#navlinks                   { margin-bottom:1em; }
-.navlink                    { }
-.navlinkcurrent             { font-weight:bold; }
-.form                       { margin:0em; }
-
-#searchformdiv              { margin:0 0 1em 0; }
-#searchform                 { margin:0; }
-#searchform span            { padding:4px; }
-#stopfilterlink             { font-weight:bold; }
-.filtering                  { font-weight:bold; }
-
-#main .journal { }
-#main .register { }
-.current                    { font-weight:bold; }
-.date                       { padding-left:0em; }
-.description                { padding-left:1em; white-space:normal; }
-.account                    { padding-left:1em; white-space:normal; }
-.amount                     { padding-left:1em; white-space:nowrap; }
-.balance                    { padding-left:1em; padding-right:0.3em; white-space:nowrap; }
-.amount, .balance { width:2em; }  /* minimise width */
-.positive                   { }
-
-/* table.transactionsreport         { border-spacing: 0; } */
-/* .transactionsreport td           {  } */
-/* .transactionsreport pre          { margin-top:0; } */
-
-/* table.entriesreport         { border-spacing: 0; } */
-/* .entriesreport td           {  } */
-/* .entriesreport pre          { margin-top:0; } */
-
-.balancereport              { border-spacing:0; }
-.balancereport tr           { vertical-align:bottom; border-spacing:0; }
-.balancereport .title       { white-space:nowrap; }
-.balancereport .item        { }
-/* .balancereport .depth0      { padding-top:1em; } */
-.balancereport td           { padding:0; }
-.totalrule td               { border-top:thin solid black; }
-.balancereport .account     { white-space:nowrap; }
-
-.hidden { display:none; }
-table.registerreport        { border-spacing:0; }
-table.registerreport tr     { vertical-align:top; }
-table.registerreport td     { padding-bottom:0.2em; }
-table.registerreport .date  { white-space:nowrap; }
-table.registerreport tr.posting { font-size:smaller; }
-table.registerreport tr.posting .account  { padding-left:1.5em; }
-table.registerreport tr.posting .amount  { padding-right:0.5em; }
-tr.firstposting td          { }
-/* tr.newday td                { border-top: 1px solid #797; } */
-/* tr.newday .date             { font-weight:bold; } */
-/* tr.newmonth td              { border-top: 2px solid #464; } */
-/* tr.newyear td               { border-top: 3px solid black; } */
-#accountsheading            { white-space:nowrap; }
-
-
-#addform {
-  /* margin:0 0 2em; */
-  /* padding:.5em 0; */
-  /* border-top:thin solid #e0e0e0; */
-  /* border-bottom:thin solid #e0e0e0; */
-}
-#addform tr {
-  vertical-align:top;
-}
-/* #addform input.textinput, #addform .dhx_combo_input, .dhx_combo_list   { padding:4px; } */
-/* #addform table              { } */
-/* #addform #addbuttonrow      { text-align:right; } */
-/* #editform                   { width:95%; } */
-#editform textarea          { width:100%; padding:4px; }
-/* #searchform table           { border-spacing:0; padding-left:0em; } */
-
 ::-moz-placeholder {
   font-style:italic;
 }
@@ -259,4 +91,189 @@
 
 .twitter-typeahead {
   width:100%;
+}
+
+code {
+  font-weight: bold;
+  color: black;
+}
+
+ul {
+    list-style-type: none;
+	  padding: 0;
+}
+
+#main-content {
+    padding-left: 30px;
+}
+
+#sidebar-menu {
+    overflow:hidden;
+    border-right: 1px solid #ebebeb;
+}
+#sidebar-menu .main-menu {
+    table-layout: fixed;
+    word-wrap: break-word;
+}
+
+#sidebar-menu .main-menu td {
+    padding: 1px !important;
+    border-top: 1px solid #ebebeb;
+    overflow: hidden;
+    white-space:nowrap;
+    text-overflow:ellipsis;
+    font-size: 16px;
+}
+
+#sidebar-menu .main-menu .ff-wrapper { /* This wrapper is needed because firefox won't apply overflow to a td-tag */
+    overflow:hidden;
+    text-overflow:ellipsis;
+}
+
+#sidebar-menu .main-menu .top {
+    border: none !important;
+}
+
+#sidebar-menu .main-menu a {
+    display: inline;
+	  font-size: 16px;
+	  font-weight: 500;
+	  color: #2F2F2F;
+	  padding: 4px 20px;
+}
+
+#sidebar-menu .main-menu a:hover {
+	  color: #11427D;
+	  text-decoration: none;
+	  background-color: transparent;
+}
+
+#sidebar-menu .main-menu .only{
+    visibility: hidden;
+    padding: 1px;
+}
+
+#sidebar-menu .main-menu tr:hover > td > div > .only {
+    visibility: visible;
+}
+
+#sidebar-menu .main-menu .only:hover{
+    border-left: none;
+}
+#sidebar-menu .main-menu .balance {
+    float: right;
+}
+
+#sidebar-menu .main-menu .total {
+    border-left: none;
+    border-right: none;
+    border-bottom: none;
+    border-top: 1px solid black;
+}
+
+#sidebar-menu .main-menu .inacct {
+    font-weight: bold;
+	  color: #11427D;
+    background-color: #f9f9f9;
+}
+
+#sidebar-menu .main-menu .amount {
+    float: right;
+    overflow-x:auto;
+	  font-weight: 500 !important;
+}
+
+#sidebar-menu .main-menu .acct {
+    width:60%;
+    vertical-align:bottom;
+}
+
+.transactionsreport .nonhead {
+    border: none !important;
+}
+
+.negative {
+    color: #a94442;
+}
+
+.date {
+    whitespace: nowrap;
+}
+
+#main-content {
+   /* 
+    -webkit-transition: width 0.3s ease, margin 0.3s ease;
+    -moz-transition: width 0.3s ease, margin 0.3s ease;
+    -o-transition: width 0.3s ease, margin 0.3s ease;
+    transition: width 0.3s ease, margin 0.3s ease;
+*/
+}
+
+#sidebar-menu {
+    /*
+    -webkit-transition: width 0.3s ease, margin 0.3s ease,opacity 0.3s ease,height 1s ease 1s;
+    -moz-transition: width 0.3s ease, margin 0.3s ease,opacity 0.3s ease,height 1s ease 1s;
+    -o-transition: width 0.3s ease, margin 0.3s ease,opacity 0.3s ease,height 1s ease 1s;
+    transition: width 0.3s ease, margin 0.3s ease,opacity 0.3s ease,height 1s ease 1s;
+*/
+}
+
+.col-any-0 {
+    width:0 !important;
+    height:0 !important;
+    float: left;
+    opacity:0;
+}
+
+.tgl-icon {
+    font-size:large;
+}
+
+#searchbar {
+    width: 100% !important;
+}
+
+@media screen and (max-width: 768px) {
+    .row-offcanvas {
+        position: relative;
+        -webkit-transition: all .25s ease-out;
+        -o-transition: all .25s ease-out;
+        transition: all .25s ease-out;
+    }
+
+    .row-offcanvas-right {
+        right: 0;
+    }
+
+    .row-offcanvas-left {
+        left: 0;
+    }
+
+    .row-offcanvas-right
+    .sidebar-offcanvas {
+        right: -90%;
+    }
+
+    .row-offcanvas-left
+    .sidebar-offcanvas {
+        left: -90%;
+    }
+
+    .row-offcanvas-right.active {
+        right: 90%;
+    }
+
+    .row-offcanvas-left.active {
+        left: 90%;
+    }
+
+    .sidebar-offcanvas {
+        position: absolute;
+        top: 0;
+        width: 90%;
+    }
+
+    #sidebar-menu .main-menu td {
+        max-width: 200px;
+    }
 }
diff --git a/static/hledger.js b/static/hledger.js
--- a/static/hledger.js
+++ b/static/hledger.js
@@ -5,6 +5,12 @@
 
 $(document).ready(function() {
 
+  // ensure add form always focusses its first field
+  $('#addmodal')
+    .on('shown.bs.modal', function (e) {
+      addformFocus();
+    })
+
   // show add form if ?add=1
   if ($.url.param('add')) { addformShow(true); }
 
@@ -24,8 +30,20 @@
   $('body, #addform input, #addform select').bind('keydown', 'ctrl+shift+=', addformAddPosting);
   $('body, #addform input, #addform select').bind('keydown', 'ctrl+=',       addformAddPosting);
   $('body, #addform input, #addform select').bind('keydown', 'ctrl+-',       addformDeletePosting);
-  $('#addform tr.posting:last > td:first input').bind('keydown', 'tab', addformAddPostingWithTab);
+  $('.amount-input:last').keypress(addformAddPosting);
 
+
+  // highlight the entry from the url hash
+  if (window.location.hash && $(window.location.hash)[0]) {
+    $(window.location.hash).addClass('highlighted');
+  }
+  $(window).on('hashchange', function(event) {
+    $('.highlighted').removeClass('highlighted');
+    $(window.location.hash).addClass('highlighted');
+  });
+  $('[data-toggle="offcanvas"]').click(function () {
+      $('.row-offcanvas').toggleClass('active');
+  });
 });
 
 //----------------------------------------------------------------------
@@ -36,58 +54,39 @@
   return $container.plot(
     series,
     { /* general chart options */
-      // series: {
-      // },
-      // yaxis: {
-      //   /* ticks: 6, */
-      // },
       xaxis: {
         mode: "time",
         timeformat: "%Y/%m/%d"
-        /* ticks: 6, */
       },
+      legend: {
+        position: 'sw'
+      },
       grid: {
         markings:
          function (axes) {
           var now = Date.now();
           var markings = [
-            // {
-            //   xaxis: { to: now },        // past
-            //   yaxis: { from: 0, to: 0 }, // =0
-            //  color: '#d88',
-            //  lineWidth:1
-            // },
             {
               xaxis: { to: now }, // past
               yaxis: { to: 0 },   // <0
               color: '#ffdddd',
             },
-
-            // {
-            //   xaxis: { from: now, to: now }, // now
-            //  color: '#bbb',
-            // },
-
             {
               xaxis: { from: now }, // future
               yaxis: { from: 0 },   // >0
-              // color: '#dddddd',
               color: '#e0e0e0',
             },
             {
               xaxis: { from: now }, // future
               yaxis: { to: 0 },     // <0
-              // color: '#ddbbbb',
               color: '#e8c8c8',
             },
             {
-              // xaxis: { from: now },      // future
               yaxis: { from: 0, to: 0 }, // =0
               color: '#bb0000',
               lineWidth:1
             },
           ];
-          // console.log(markings);
           return markings;
         },
         hoverable: true,
@@ -113,13 +112,14 @@
 
 function registerChartClick(ev, pos, item) {
   if (item) {
-    var date = $.plot.dateGenerator(item.datapoint[0], {});
-    var dateid = $.plot.formatDate(date, '%Y-%m-%d');
-    $target = $('#'+dateid);
-    if ($target.length)
+    targetselector = '#'+item.series.data[item.dataIndex][5];
+    $target = $(targetselector);
+    if ($target.length) {
+      window.location.hash = targetselector;
       $('html, body').animate({
         scrollTop: $target.offset().top
       }, 1000);
+    }
   }
 }
 
@@ -129,11 +129,7 @@
 function addformShow(showmsg) {
   showmsg = typeof showmsg !== 'undefined' ? showmsg : false;
   addformReset(showmsg);
-  $('#addmodal')
-    .on('shown.bs.modal', function (e) {
-      addformFocus();
-    })
-    .modal('show');
+  $('#addmodal').modal('show');
 }
 
 // Make sure the add form is empty and clean for display.
@@ -145,7 +141,7 @@
     // reset typehead state (though not fetched completions)
     $('.typeahead').typeahead('val', '');
     $('.tt-dropdown-menu').hide();
-    $('input#date').val('today');
+    $('input#date').val(''); // #322 don't set a default, typeahead(?) clears it on tab. See also Foundation.hs
   }
 }
 
@@ -163,44 +159,21 @@
 
 // Insert another posting row in the add form.
 function addformAddPosting() {
+  $('.amount-input:last').off('keypress');
   // do nothing if it's not currently visible
   if (!$('#addform').is(':visible')) return;
   // save a copy of last row
-  var lastrow = $('#addform tr.posting:last').clone();
+  var lastrow = $('#addform .form-group:last').clone();
 
   // replace the submit button with an amount field, clear and renumber it, add the keybindings
-  $('#addform tr.posting:last > td:last')
-    .html( $('#addform tr.posting:first > td:last').html() );
-  var num = $('#addform tr.posting').length;
-  $('#addform tr.posting:last > td:last input:last') // input:last here and elsewhere is to avoid autocomplete's extra input
-    .val('')
-    .prop('id','amount'+num)
-    .prop('name','amount'+num)
-    .prop('placeholder','Amount '+num)
-    .bind('keydown', 'ctrl+shift+=', addformAddPosting)
-    .bind('keydown', 'ctrl+=', addformAddPosting)
-    .bind('keydown', 'ctrl+-', addformDeletePosting);
-
-  // set up the new last row's account field.
-  // First typehead, it's hard to enable on new DOM elements
-  var $acctinput = lastrow.find('.account-input:last');
-  // XXX nothing works
-  // $acctinput.typeahead('destroy'); //,'NoCached');
-  // lastrow.on("DOMNodeInserted", function () {
-  //   //$(this).find(".typeahead").typeahead();
-  //   console.log('DOMNodeInserted');
-  //  // infinite loop
-  //  console.log($(this).find('.typeahead'));
-  //   //enableTypeahead($(this).find('.typeahead'), accountsSuggester);
-  // });
-  // setTimeout(function (){
-  //   $('#addform tr.posting:last input.account-input').typeahead('destroy');
-  //   enableTypeahead($('#addform tr.posting:last input.account-input:last'), accountsSuggester);
-  // }, 1000);
+  var num = $('#addform .account-group').length;
 
   // insert the new last row
-  $('#addform > table > tbody').append(lastrow);
+  $('#addform .account-postings').append(lastrow);
+  // TODO: Enable typehead on dynamically created inputs
 
+  var $acctinput = $('.account-input:last');
+  var $amntinput = $('.amount-input:last');
   // clear and renumber the field, add keybindings
   $acctinput
     .val('')
@@ -211,77 +184,47 @@
   $acctinput
     .bind('keydown', 'ctrl+shift+=', addformAddPosting)
     .bind('keydown', 'ctrl+=', addformAddPosting)
-    .bind('keydown', 'ctrl+-', addformDeletePosting)
-    .bind('keydown', 'tab', addformAddPostingWithTab);
-}
+    .bind('keydown', 'ctrl+-', addformDeletePosting);
 
-// Insert another posting row by tabbing within the last field, also advancing the focus.
-function addformAddPostingWithTab(ev) {
-  // do nothing if called from a non-last row (don't know how to remove keybindings)
-  if ($(ev.target).is('#addform input.account-input:last')) {
-    addformAddPosting();
-    focus($('#addform input.amount-input:last')); // help FF
-    return false;
-  }
-  else
-    return true;
+  $amntinput
+    .val('')
+    .prop('id','amount'+(num+1))
+    .prop('name','amount'+(num+1))
+    .prop('placeholder','Amount '+(num+1))
+    .keypress(addformAddPosting);
+
+  $acctinput
+    .bind('keydown', 'ctrl+shift+=', addformAddPosting)
+    .bind('keydown', 'ctrl+=', addformAddPosting)
+    .bind('keydown', 'ctrl+-', addformDeletePosting);
+
 }
 
 // Remove the add form's last posting row, if empty, keeping at least two.
 function addformDeletePosting() {
-  var num = $('#addform tr.posting').length;
-  if (num <= 2
-      || $('#addform tr.posting:last > td:first input:last').val() != ''
-     ) return;
-  // copy submit button
-  var btn = $('#addform tr.posting:last > td:last').html();
+  var num = $('#addform .account-group').length;
+  if (num <= 2) return;
   // remember if the last row's field or button had focus
   var focuslost =
-    $('#addform tr.posting:last > td:first input:last').is(':focus')
-    || $('#addform tr.posting:last button').is(':focus');
+    $('.account-input:last').is(':focus')
+    || $('.amount-input:last').is(':focus');
   // delete last row
-  $('#addform tr.posting:last').remove();
-  // remember if the last amount field had focus
-  focuslost = focuslost || 
-    $('#addform tr.posting:last > td:last input:last').is(':focus');
-  // replace new last row's amount field with the button
-  $('#addform tr.posting:last > td:last').css('text-align','right').html(btn);
-  // if deleted row had focus, focus the new last row
-  if (focuslost) $('#addform tr.posting:last > td:first input:last').focus();
+  $('#addform .account-group:last').remove();
+  if(focuslost){
+    focus($('account-input:last'));
+  }
+  // Rebind keypress
+  $('.amount-input:last').keypress(addformAddPosting);
 }
 
 //----------------------------------------------------------------------
 // SIDEBAR
 
 function sidebarToggle() {
-  //console.log('sidebarToggle');
-  var visible = $('#sidebar').is(':visible');
-  //console.log('sidebar visibility was',visible);
-  // if opening sidebar, start an ajax fetch of its content
-  if (!visible) {
-    //console.log('getting sidebar content');
-    $.get("sidebar"
-         ,null
-         ,function(data) {
-            //console.log( "success" );
-            $("#sidebar-body" ).html(data);
-          })
-          .done(function() {
-            //console.log( "success 2" );
-          })
-          .fail(function() {
-            //console.log( "error" );
-          });
-  }
-  // localStorage.setItem('sidebarVisible', !visible);
-  // set a cookie to communicate the new sidebar state to the server
-  $.cookie('showsidebar', visible ? '0' : '1');
-  // horizontally slide the sidebar in or out
-  // how to make it smooth, without delayed content pop-in ?
-  //$('#sidebar').animate({'width': 'toggle'});
-  //$('#sidebar').animate({'width': visible ? 'hide' : '+=20m'});
-  //$('#sidebar-spacer').width(200);
-  $('#sidebar').animate({'width': visible ? 'hide' : 'show'});
+  $('#sidebar-menu').toggleClass('col-md-4 col-sm-4 col-any-0');
+  $('#main-content').toggleClass('col-md-8 col-sm-8 col-md-12 col-sm-12');
+  $('#spacer').toggleClass('col-md-4 col-sm-4 col-any-0');
+  $.cookie('showsidebar', $('#sidebar-menu').hasClass('col-any-0') ? '0' : '1');
 }
 
 //----------------------------------------------------------------------
diff --git a/templates/default-layout-wrapper.hamlet b/templates/default-layout-wrapper.hamlet
--- a/templates/default-layout-wrapper.hamlet
+++ b/templates/default-layout-wrapper.hamlet
@@ -25,9 +25,8 @@
           document.hledgerWebBaseurl = '#{base_url_ opts}'; // save for binding keys later
 
     <body>
-        <div class="container">
-            <header>
-            <div id="outermain" role="main">
+        <div .container-fluid>
+            <div .row .row-offcanvas .row-offcanvas-left>
               ^{pageBody pc}
             <footer>
                 #{extraCopyright $ appExtra $ settings master}
@@ -61,11 +60,11 @@
                     <p>
                       <b>Keyboard shortcuts
                       <ul>
-                        <li> <b><tt>h</tt></b> or maybe <b><tt>?</tt></b> - view this help (escape or click to exit)
-                        <li> <b><tt>j</tt></b> - go to the Journal view (home)
-                        <li> <b><tt>a</tt></b> - add a transaction (escape to cancel)
-                        <li> <b><tt>s</tt></b> - toggle sidebar
-                        <li> <b><tt>f</tt></b> - focus search form ("find")
+                        <li> <code>h</code> or maybe <code>?</code> - view this help (escape or click to exit)
+                        <li> <code>j</code> - go to the Journal view (home)
+                        <li> <code>a</code> - add a transaction (escape to cancel)
+                        <li> <code>s</code> - toggle sidebar
+                        <li> <code>f</code> - focus search form ("find")
                     <p>
                       <b>General
                       <ul>
@@ -79,19 +78,19 @@
                     <p>
                       <b>Search
                       <ul>
-                        <li> <b><tt>acct:REGEXP</tt></b> - filter on to/from account
-                        <li> <b><tt>desc:REGEXP</tt></b> - filter on description
-                        <li> <b><tt>date:PERIODEXP</tt></b>, <b><tt>date2:PERIODEXP</tt></b> - filter on date or secondary date
-                        <li> <b><tt>code:REGEXP</tt></b> - filter on transaction's code (eg check number)
-                        <li> <b><tt>status:*</tt></b>, <b><tt>status:!</tt></b>, <b><tt>status:</tt></b> - filter on transaction's cleared status (cleared, pending, uncleared)
-                        <!-- <li> <b><tt>empty:BOOL</tt></b> - filter on whether amount is zero -->
-                        <li> <b><tt>amt:N</tt></b>, <b><tt>amt:&lt;N</tt></b>, <b><tt>amt:&gt;N</tt></b> - filter on the unsigned amount magnitude. Or with a sign before N, filter on the signed value. (Single-commodity amounts only.)
-                        <li> <b><tt>cur:REGEXP</tt></b> - filter on the currency/commodity symbol (must match all of it). Dollar sign must be written as <tt>\$</tt>
-                        <li> <b><tt>tag:NAME</tt></b>, <b><tt>tag:NAME=REGEX</tt></b> - filter on tag name, or tag name and value
-                        <!-- <li> <b><tt>depth:N</tt></b> - filter out accounts below this depth -->
-                        <li> <b><tt>real:BOOL</tt></b> - filter on postings' real/virtual-ness
+                        <li> <code>acct:REGEXP</code> - filter on to/from account
+                        <li> <code>desc:REGEXP</code> - filter on description
+                        <li> <code>date:PERIODEXP</code>, <code>date2:PERIODEXP</code> - filter on date or secondary date
+                        <li> <code>code:REGEXP</code> - filter on transaction's code (eg check number)
+                        <li> <code>status:*</code>, <code>status:!</code>, <code>status:</code> - filter on transaction's cleared status (cleared, pending, uncleared)
+                        <!-- <li> <code>empty:BOOL</code> - filter on whether amount is zero -->
+                        <li> <code>amt:N</code>, <code>amt:&lt;N</code>, <code>amt:&gt;N</code> - filter on the unsigned amount magnitude. Or with a sign before N, filter on the signed value. (Single-commodity amounts only.)
+                        <li> <code>cur:REGEXP</code> - filter on the currency/commodity symbol (must match all of it). Dollar sign must be written as <code>\$</code>
+                        <li> <code>tag:NAME</code>, <code>tag:NAME=REGEX</code> - filter on tag name, or tag name and value
+                        <!-- <li> <code>depth:N</code> - filter out accounts below this depth -->
+                        <li> <code>real:BOOL</code> - filter on postings' real/virtual-ness
                         <li> Enclose search patterns containing spaces in single or double quotes
-                        <li> Prepend <b><tt>not:</tt></b> to negate a search term
+                        <li> Prepend <code>not:</code> to negate a search term
                         <li> Multiple search terms on different fields are AND'ed, multiple search terms on the same field are OR'ed
                         <li> These search terms also work with command-line hledger
 
diff --git a/templates/homepage.hamlet b/templates/homepage.hamlet
deleted file mode 100644
--- a/templates/homepage.hamlet
+++ /dev/null
@@ -1,38 +0,0 @@
-<h1>_{MsgHello}
-
-<ol>
-  <li>Now that you have a working project you should use the #
-    \<a href="http://www.yesodweb.com/book/">Yesod book</a> to learn more. #
-    You can also use this scaffolded site to explore some basic concepts.
-
-  <li> This page was generated by the #{handlerName} handler in #
-    \<em>Handler/Home.hs</em>.
-
-  <li> The #{handlerName} handler is set to generate your site's home screen in Routes file #
-    <em>config/routes
-
-  <li> The HTML you are seeing now is actually composed by a number of <em>widgets</em>, #
-    most of them are brought together by the <em>defaultLayout</em> function which #
-    is defined in the <em>Foundation.hs</em> module, and used by <em>#{handlerName}</em>. #
-    All the files for templates and wigdets are in <em>templates</em>.
-
-  <li>
-    A Widget's Html, Css and Javascript are separated in three files with the #
-    \<em>.hamlet</em>, <em>.lucius</em> and <em>.julius</em> extensions. 
-
-  <li ##{aDomId}>If you had javascript enabled then you wouldn't be seeing this.
-    
-  <li #form>
-    This is an example trivial Form. Read the #
-    \<a href="http://www.yesodweb.com/book/forms">Forms chapter</a> #
-    on the yesod book to learn more about them.
-    $maybe (info,con) <- submission
-      <div .message>
-        Your file's type was <em>#{fileContentType info}</em>. You say it has: <em>#{con}</em>
-    <form method=post action=@{HomeR}#form enctype=#{formEnctype}>
-      ^{formWidget}
-      <input type="submit" value="Send it!">
-
-  <li> And last but not least, Testing. In <em>tests/main.hs</em> you will find a #
-    test suite that performs tests on this page. #
-    You can run your tests by doing: <pre>yesod test</pre>
diff --git a/templates/homepage.julius b/templates/homepage.julius
deleted file mode 100644
--- a/templates/homepage.julius
+++ /dev/null
@@ -1,1 +0,0 @@
-document.getElementById("#{aDomId}").innerHTML = "This text was added by the Javascript part of the homepage widget.";
diff --git a/templates/homepage.lucius b/templates/homepage.lucius
deleted file mode 100644
--- a/templates/homepage.lucius
+++ /dev/null
@@ -1,6 +0,0 @@
-h1 {
-    text-align: center
-}
-h2##{aDomId} {
-    color: #990
-}
diff --git a/templates/normalize.lucius b/templates/normalize.lucius
deleted file mode 100644
--- a/templates/normalize.lucius
+++ /dev/null
@@ -1,439 +0,0 @@
-/*! normalize.css 2011-08-12T17:28 UTC · http://github.com/necolas/normalize.css */
-
-/* =============================================================================
-   HTML5 display definitions
-   ========================================================================== */
-
-/*
- * Corrects block display not defined in IE6/7/8/9 & FF3
- */
-
-article,
-aside,
-details,
-figcaption,
-figure,
-footer,
-header,
-hgroup,
-nav,
-section {
-    display: block;
-}
-
-/*
- * Corrects inline-block display not defined in IE6/7/8/9 & FF3
- */
-
-audio,
-canvas,
-video {
-    display: inline-block;
-    *display: inline;
-    *zoom: 1;
-}
-
-/*
- * Prevents modern browsers from displaying 'audio' without controls
- */
-
-audio:not([controls]) {
-    display: none;
-}
-
-/*
- * Addresses styling for 'hidden' attribute not present in IE7/8/9, FF3, S4
- * Known issue: no IE6 support
- */
-
-[hidden] {
-    display: none;
-}
-
-
-/* =============================================================================
-   Base
-   ========================================================================== */
-
-/*
- * 1. Corrects text resizing oddly in IE6/7 when body font-size is set using em units
- *    http://clagnut.com/blog/348/#c790
- * 2. Keeps page centred in all browsers regardless of content height
- * 3. Prevents iOS text size adjust after orientation change, without disabling user zoom
- *    www.456bereastreet.com/archive/201012/controlling_text_size_in_safari_for_ios_without_disabling_user_zoom/
- */
-
-html {
-    font-size: 100%; /* 1 */
-    overflow-y: scroll; /* 2 */
-    -webkit-text-size-adjust: 100%; /* 3 */
-    -ms-text-size-adjust: 100%; /* 3 */
-}
-
-/*
- * Addresses margins handled incorrectly in IE6/7
- */
-
-body {
-    margin: 0;
-}
-
-/* 
- * Addresses font-family inconsistency between 'textarea' and other form elements.
- */
-
-body,
-button,
-input,
-select,
-textarea {
-    font-family: sans-serif;
-}
-
-
-/* =============================================================================
-   Links
-   ========================================================================== */
-
-a {
-    color: #00e;
-}
-
-a:visited {
-    color: #551a8b;
-}
-
-/*
- * Addresses outline displayed oddly in Chrome
- */
-
-a:focus {
-    outline: thin dotted;
-}
-
-/*
- * Improves readability when focused and also mouse hovered in all browsers
- * people.opera.com/patrickl/experiments/keyboard/test
- */
-
-a:hover,
-a:active {
-    outline: 0;
-}
-
-
-/* =============================================================================
-   Typography
-   ========================================================================== */
-
-/*
- * Addresses styling not present in IE7/8/9, S5, Chrome
- */
-
-abbr[title] {
-    border-bottom: 1px dotted;
-}
-
-/*
- * Addresses style set to 'bolder' in FF3/4, S4/5, Chrome
-*/
-
-b, 
-strong { 
-    font-weight: bold; 
-}
-
-blockquote {
-    margin: 1em 40px;
-}
-
-/*
- * Addresses styling not present in S5, Chrome
- */
-
-dfn {
-    font-style: italic;
-}
-
-/*
- * Addresses styling not present in IE6/7/8/9
- */
-
-mark {
-    background: #ff0;
-    color: #000;
-}
-
-/*
- * Corrects font family set oddly in IE6, S4/5, Chrome
- * en.wikipedia.org/wiki/User:Davidgothberg/Test59
- */
-
-pre,
-code,
-kbd,
-samp {
-    font-family: monospace, serif;
-    _font-family: 'courier new', monospace;
-    font-size: 1em;
-}
-
-/*
- * Improves readability of pre-formatted text in all browsers
- */
-
-pre {
-    white-space: pre;
-    white-space: pre-wrap;
-    word-wrap: break-word;
-}
-
-/*
- * 1. Addresses CSS quotes not supported in IE6/7
- * 2. Addresses quote property not supported in S4
- */
-
-/* 1 */
-
-q {
-    quotes: none;
-}
-
-/* 2 */
-
-q:before,
-q:after {
-    content: '';
-    content: none;
-}
-
-small {
-    font-size: 75%;
-}
-
-/*
- * Prevents sub and sup affecting line-height in all browsers
- * gist.github.com/413930
- */
-
-sub,
-sup {
-    font-size: 75%;
-    line-height: 0;
-    position: relative;
-    vertical-align: baseline;
-}
-
-sup {
-    top: -0.5em;
-}
-
-sub {
-    bottom: -0.25em;
-}
-
-
-/* =============================================================================
-   Lists
-   ========================================================================== */
-
-ul,
-ol {
-    margin: 1em 0;
-    padding: 0 0 0 40px;
-}
-
-dd {
-    margin: 0 0 0 40px;
-}
-
-nav ul,
-nav ol {
-    list-style: none;
-    list-style-image: none;
-}
-
-
-/* =============================================================================
-   Embedded content
-   ========================================================================== */
-
-/*
- * 1. Removes border when inside 'a' element in IE6/7/8/9
- * 2. Improves image quality when scaled in IE7
- *    code.flickr.com/blog/2008/11/12/on-ui-quality-the-little-things-client-side-image-resizing/
- */
-
-img {
-    border: 0; /* 1 */
-    -ms-interpolation-mode: bicubic; /* 2 */
-}
-
-/*
- * Corrects overflow displayed oddly in IE9 
- */
-
-svg:not(:root) {
-    overflow: hidden;
-}
-
-
-/* =============================================================================
-   Figures
-   ========================================================================== */
-
-/*
- * Addresses margin not present in IE6/7/8/9, S5, O11
- */
-
-figure {
-    margin: 0;
-}
-
-
-/* =============================================================================
-   Forms
-   ========================================================================== */
-
-/*
- * Corrects margin displayed oddly in IE6/7
- */
-
-form {
-    margin: 0;
-}
-
-/*
- * Define consistent margin and padding
- */
-
-fieldset {
-    margin: 0 2px;
-    padding: 0.35em 0.625em 0.75em;
-}
-
-/*
- * 1. Corrects color not being inherited in IE6/7/8/9
- * 2. Corrects alignment displayed oddly in IE6/7
- */
-
-legend {
-    border: 0; /* 1 */
-    *margin-left: -7px; /* 2 */
-}
-
-/*
- * 1. Corrects font size not being inherited in all browsers
- * 2. Addresses margins set differently in IE6/7, F3/4, S5, Chrome
- * 3. Improves appearance and consistency in all browsers
- */
-
-button,
-input,
-select,
-textarea {
-    font-size: 100%; /* 1 */
-    margin: 0; /* 2 */
-    vertical-align: baseline; /* 3 */
-    *vertical-align: middle; /* 3 */
-}
-
-/*
- * 1. Addresses FF3/4 setting line-height using !important in the UA stylesheet
- * 2. Corrects inner spacing displayed oddly in IE6/7
- */
-
-button,
-input {
-    line-height: normal; /* 1 */
-    *overflow: visible;  /* 2 */
-}
-
-/*
- * Corrects overlap and whitespace issue for buttons and inputs in IE6/7
- * Known issue: reintroduces inner spacing
- */
-
-table button,
-table input {
-    *overflow: auto;
-}
-
-/*
- * 1. Improves usability and consistency of cursor style between image-type 'input' and others
- * 2. Corrects inability to style clickable 'input' types in iOS
- */
-
-button,
-html input[type="button"], 
-input[type="reset"], 
-input[type="submit"] {
-    cursor: pointer; /* 1 */
-    -webkit-appearance: button; /* 2 */
-}
-
-/*
- * 1. Addresses box sizing set to content-box in IE8/9
- * 2. Addresses excess padding in IE8/9
- */
-
-input[type="checkbox"],
-input[type="radio"] {
-    box-sizing: border-box; /* 1 */
-    padding: 0; /* 2 */
-}
-
-/*
- * 1. Addresses appearance set to searchfield in S5, Chrome
- * 2. Addresses box sizing set to border-box in S5, Chrome (include -moz to future-proof)
- */
-
-input[type="search"] {
-    -webkit-appearance: textfield; /* 1 */
-    -moz-box-sizing: content-box;
-    -webkit-box-sizing: content-box; /* 2 */
-    box-sizing: content-box;
-}
-
-/*
- * Corrects inner padding displayed oddly in S5, Chrome on OSX
- */
-
-input[type="search"]::-webkit-search-decoration {
-    -webkit-appearance: none;
-}
-
-/*
- * Corrects inner padding and border displayed oddly in FF3/4
- * www.sitepen.com/blog/2008/05/14/the-devils-in-the-details-fixing-dojos-toolbar-buttons/
- */
-
-button::-moz-focus-inner,
-input::-moz-focus-inner {
-    border: 0;
-    padding: 0;
-}
-
-/*
- * 1. Removes default vertical scrollbar in IE6/7/8/9
- * 2. Improves readability and alignment in all browsers
- */
-
-textarea {
-    overflow: auto; /* 1 */
-    vertical-align: top; /* 2 */
-}
-
-
-/* =============================================================================
-   Tables
-   ========================================================================== */
-
-/* 
- * Remove most spacing between table cells
- */
-
-table {
-    border-collapse: collapse;
-    border-spacing: 0;
-}
