overhaul using augmented-ui
authorxangelo <git@xangelo.ca>
Mon, 9 May 2022 05:48:02 +0000 (01:48 -0400)
committerxangelo <git@xangelo.ca>
Mon, 9 May 2022 15:28:38 +0000 (11:28 -0400)
This is a major overhau of the UI to something that fits the new theme
... SPACE!

overall the design is very simplistic with a fairly muted background to
allow for focus on the gameplay.

public/assets/bg/green_nebula_4.png [new file with mode: 0644]
public/game.html
public/index.html
public/scifi.css [new file with mode: 0644]
src/api.ts
src/render/fight.ts
src/render/kingdom-overview.ts
src/render/land-development.ts
src/render/mail.ts
src/render/unit-training.ts

diff --git a/public/assets/bg/green_nebula_4.png b/public/assets/bg/green_nebula_4.png
new file mode 100644 (file)
index 0000000..e8cdb3f
Binary files /dev/null and b/public/assets/bg/green_nebula_4.png differ
index 2eaff164096ead63f4524e73bec3424f036b5967..07e38fb51980ac520a20e16ec99ada508a1edddb 100644 (file)
@@ -1,13 +1,54 @@
 <!doctype html>
 <html>
-    <head>
-        <title>Tick City</title>
-        <meta charset="utf-8">
-        <script src="https://unpkg.com/htmx.org@1.7.0" integrity="sha384-EzBXYPt0/T6gxNp0nuPtLkmRpmDBbjg6WmCUZRLXBBwYYmwAUxzlSGej0ARHX0Bo" crossorigin="anonymous" defer></script>
-        <link rel="stylesheet" href="https://fonts.xz.style/serve/inter.css">
-        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css">
-        <link rel="stylesheet" href="/stylesheet.css">
-    </head>
-    <body hx-trigger="load" hx-get="/city">
-    </body>
+
+<head>
+    <title>Tick City</title>
+    <meta charset="utf-8">
+    <link rel="preconnect" href="https://fonts.googleapis.com">
+    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+    <link href="https://fonts.googleapis.com/css2?family=Roboto+Condensed:wght@300;400;700&display=swap"
+        rel="stylesheet">
+    <script src="https://unpkg.com/htmx.org@1.7.0"
+        integrity="sha384-EzBXYPt0/T6gxNp0nuPtLkmRpmDBbjg6WmCUZRLXBBwYYmwAUxzlSGej0ARHX0Bo" crossorigin="anonymous"
+        defer></script>
+    <link rel="stylesheet" type="text/css" href="https://unpkg.com/augmented-ui@2/augmented-ui.min.css">
+    <link rel="stylesheet" href="/scifi.css">
+    <link rel="stylesheet" href="/stylesheet.css">
+
+</head>
+
+<body>
+    <div class="row">
+        <div class="col sidebar">
+            <div class="row">
+                <div class="col pane" data-augmented-ui="tl-clip tr-2-clip-x br-2-clip-y bl-clip-x border"> 
+                    <h2 data-augmented-ui="tl-clip bl-clip none">Nav</h2>
+                    <ul class="nav">
+                        <li>
+                            <a href="#" hx-target="#main" hx-get="/poll/overview" hx-trigger="load, click">Home</a>
+                        </li>
+                        <li>
+                            <a href="#" hx-target="#main" hx-get="/poll/construction" hx-trigger="click">Construction</a>
+                        </li>
+                        <li>
+                            <a href="#" hx-target="#main" hx-get="/poll/unit-training" hx-trigger="click">Unit Training</a>
+                        </li>
+                        <li>
+                            <a href="#" hx-target="#main" hx-get="/poll/map" hx-trigger="click">Map</a>
+                        </li>
+                        <li>
+                            <a href="#" hx-target="#main" hx-get="/poll/mailroom" hx-trigger="click">Mail</a>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+        </div>
+        <div class="col" id="main" data-augmented-ui="tl-clip tr-clip br-clip-x bl-clip border">
+
+        </div>
+    </div>
+    <footer>
+        A project by <a href="https://xangelo.ca">xangelo</a>
+    </footer>
+</body>
 </html>
\ No newline at end of file
index 80324ebbef359384c3e0f7ac7cc2da3206cef44e..8cdfc070031c32bdc5f46169b0c92f1cb8845abb 100644 (file)
@@ -1,67 +1,89 @@
 <!doctype html>
 <html>
-    <head>
-        <title>Tick City</title>
-        <meta charset="utf-8">
-        <script src="https://unpkg.com/htmx.org@1.7.0" integrity="sha384-EzBXYPt0/T6gxNp0nuPtLkmRpmDBbjg6WmCUZRLXBBwYYmwAUxzlSGej0ARHX0Bo" crossorigin="anonymous" defer></script>
-        <link rel="stylesheet" href="https://fonts.xz.style/serve/inter.css">
-        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css">
-        <link rel="stylesheet" href="/stylesheet.css">
-    </head>
-    <body id="home">
-        <h2>Alpha Kingdom Management</h2>
-        <p>Hey, welcome to this alpha version of a kingdom management game. I've always loved building browser based games and this is 
-            one in a long line. There's little to no branding just yet, just a simple time-based kingdom management game
-        </p>
 
-        <table>
-            <tr>
-                <td>
-                    <h4>Signup</h4>
-        <form hx-post="/accounts">
-            <div>
-                <label>Username: </label>
-                <input type="text" name="username">
-            </div>
-            <div>
-                <label>Password: </label>
-                <input type="password" name="password">
-            </div>
-            <button type="submit">Sign Up</button>
-        </form>
+<head>
+    <title>Tick City</title>
+    <meta charset="utf-8">
+    <link rel="preconnect" href="https://fonts.googleapis.com">
+    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+    <link href="https://fonts.googleapis.com/css2?family=Roboto+Condensed:wght@300;400;700&display=swap"
+        rel="stylesheet">
+    <script src="https://unpkg.com/htmx.org@1.7.0"
+        integrity="sha384-EzBXYPt0/T6gxNp0nuPtLkmRpmDBbjg6WmCUZRLXBBwYYmwAUxzlSGej0ARHX0Bo" crossorigin="anonymous"
+        defer></script>
+    <link rel="stylesheet" type="text/css" href="https://unpkg.com/augmented-ui@2/augmented-ui.min.css">
+    <link rel="stylesheet" href="/scifi.css">
+</head>
+
+<body id="home">
+    <h1 data-augmented-ui="tl-clip bl-clip none">Antares</h1>
+    <div class="row">
+        <div class="col">
+            <p>Welcome to <b>Antares</b>, a free-to-play browser based strategy game.</p>
+            <p>We're very early in development, but are always looking for new players to sign up and test things out. Just note
+                that the likelihood of you running into a broken feature or a bug is quite high at this point. If you do 
+                encouter a bug or issue, feel free to post in our forums and one of the devs will take a look.
+            </p>
 
-                </td>
-                <td>
-                    <h4>Login</h4>
-        <form hx-post="/login">
-            <div>
-                <label>Username: </label>
-                <input type="text" name="username">
+            <h2 data-augmented-ui="tl-clip bl-clip none">Upcoming Features</h2>
+    <h3 data-augmented-ui="tl-clip bl-clip none">Species</h3>
+    <p>
+        Work is being done to add different Species to the game to allow further customization and strategy options. 
+    </p>
+    <h3 data-augmented-ui="tl-clip bl-clip none">More Buildings</h3>
+    <p>We're pretty light on buildings at the moment but we have plans for a few more:</p>
+    <ul>
+        <li>Homes</li>
+        <li>Banks</li>
+        <li>Mills</li>
+        <li>Quarries</li>
+        <li>Spy Hideouts</li>
+        <li>Mage Towers</li>
+    </ul>
+        </div>
+        <div class="col">
+            <div class="row">
+                <div class="col pane" data-augmented-ui="tl-clip tr-2-clip-x br-2-clip-y bl-clip-x border">
+                    <h3 data-augmented-ui="tl-clip bl-clip none">Signup</h3>
+                    <form hx-post="/accounts">
+                        <div>
+                            <label>Username:</label>
+                            <input type="text" name="username">
+                        </div>
+                        <div>
+                            <label>Password:</label>
+                            <input type="password" name="password">
+                        </div>
+                        <button type="submit" class="success">Sign Up</button>
+                    </form>
+                </div>
             </div>
-            <div>
-                <label>Password: </label>
-                <input type="password" name="password">
+            <div class="row">
+                <div class="col pane" style="margin-top: 2rem; " data-augmented-ui="tl-clip tr-2-clip-x br-2-clip-y bl-clip-x border">
+                    <h3 data-augmented-ui="tl-clip bl-clip none">Login</h3>
+                    <form hx-post="/login">
+                        <div>
+                            <label>Username:</label>
+                            <input type="text" name="username">
+                        </div>
+                        <div>
+                            <label>Password:</label>
+                            <input type="password" name="password">
+                        </div>
+                        <button type="submit">Log In</button>
+                    </form>
+                </div>
+
             </div>
-            <button type="submit">Log In</button>
-        </form>
-                </td>
-            </tr>
-        </table>
+        </div>
+    </div>
+
+    <div class="row">
+    </div>
 
-        <h2>Upcoming Features</h2>
-        <h3>Fantasy Races</h3>
-        <p>All good kingdom management games need to give you the ability to pick your fantasy race. Once you decide on the race of your leader you'll be given various perks/units/buildings that differ from all the others. You'll also have access to a "Race Chat" where only other members of your chosen race can see each other speak. </p>
-        <h3>More Buildings</h3>
-        <p>We're pretty light on buildings at the moment but we have plans for a few more:</p>
-        <ul>
-            <li>Banks</li>
-            <li>Mills</li>
-            <li>Quarries</li>
-            <li>Spy Hideouts</li>
-            <li>Mage Towers</li>
-        </ul>
-        <footer>
-            A project by <a href="https://xangelo.ca">xangelo</a>
-        </footer>
-    </body>
+    
+    <footer>
+        A project by <a href="https://xangelo.ca">xangelo</a>
+    </footer>
+</body>
 </html>
\ No newline at end of file
diff --git a/public/scifi.css b/public/scifi.css
new file mode 100644 (file)
index 0000000..c7c02f2
--- /dev/null
@@ -0,0 +1,222 @@
+:root {
+    --border: #1c7282;
+    --page-bg: #061619;
+    --green-bg: #193818;
+    --green-border: #32821c;
+}
+
+input {
+    outline: none;
+}
+
+body {
+    background-color: var(--page-bg);
+    color: #fff;
+    margin: 50px auto 20px;
+    width: 1024px;
+    font-family: 'Roboto Condensed', sans-serif;
+    font-weight: normal;
+    font-size: 16px;
+    background-image: url('/assets/bg/green_nebula_4.png');
+    background-blend-mode: soft-light;
+}
+hr {
+    border-color: var(--border);
+    margin-bottom: 1.2rem;
+}
+
+h1, h2, h3 {
+    text-transform: uppercase;
+    font-weight: 300;
+    line-height: 2.2rem;
+    border: solid 0 var(--border);
+    border-left-width: 6px;
+    border-bottom-width: 2px;
+    padding-left: 10px;
+    margin-top: 0;
+    color :#6ac9db;
+    text-shadow: 0 0 2px #6ac9db;
+}
+h1 {
+    font-size: 2.2rem;
+}
+h2 {
+    font-size: 1.7rem;
+}
+h3 {
+    font-size: 1.3rem;
+}
+table {
+    width: 100%;
+    margin-bottom: 2rem;
+    border-spacing: 0;
+    border: solid 1px var(--border);
+}
+tr:nth-child(odd) td, tr:nth-child(odd) th {
+    background-color: #0d2329;
+}
+th, td {
+    padding: 0.5rem;
+}
+p, form, ul, ol {
+    line-height: 1.3rem;
+    margin: 0 2rem 2rem 2rem;
+    flex: inherit;
+}
+
+label {
+    font-weight: bold;
+    margin-right: 5px;
+}
+
+button, .btn {
+    border: solid 1px var(--border);
+    background-color: #183238;
+    color: #fff;
+    font-size: 0.8rem;
+    cursor: pointer;
+    padding: 5px 5px 5px 25px;
+    text-transform: uppercase;
+    text-align: center;
+    font-weight: bold;
+    min-width: 150px;
+}
+button::after, .btn::after {
+    content: '\27EB';
+    float: right;
+    color: var(--border);
+}
+button.danger, .btn.danger {
+    background-color: #381818;
+    border-color: #821c1c;
+}
+button.danger::after, .btn.danger::after {
+    color: #821c1c
+}
+button.success, .btn.success {
+    background-color: var(--green-bg);
+    border-color: var(--green-border);
+}
+button.success::after, .btn.success::after{
+    color: #32821c;
+}
+button:active, .btn:active, button:hover, .btn:hover {
+    background-color: #1a444c;
+}
+button.success:active, .btn.success:active, button.success:hover, .btn.success:hover {
+    background-color: #1e4c1a;
+}
+
+input[type="text"], input[type="password"], input[type="number"] {
+    border: solid 1px var(--border);
+    background-color: #183238;
+    color: #fff;
+    font-size: 0.8rem;
+    cursor: pointer;
+    min-width: 120px;
+    padding: 5px;
+}
+
+a {
+    font-weight: bold;
+    color: #fff;
+    text-decoration: none;
+}
+a::before {
+    content: '\27EA';
+}
+a::after {
+    content: '\27EB';
+}
+footer {
+    text-align: center;
+    border-top: solid 1px var(--border);
+    padding-top: 1rem;
+    margin-top: 2rem;
+}
+
+.nav {
+    padding: 0;
+    margin: 0;
+}
+.nav li {
+    list-style: none;
+}
+.nav li a {
+    display: block;
+    padding: 10px 25px;
+}
+.nav li a:hover {
+    background-color: #0d2329;
+}
+.nav li a::before {
+    content: '';
+}
+.nav li a::after {
+    float: right;
+}
+
+/** Grid Stuff **/
+.row {
+    display: flex;
+}
+.col {
+    flex-grow: 1;
+    flex-basis: 50%;
+    margin-right: 5rem;
+}
+.col:last-child {
+    margin-right: 0;
+}
+.col p {
+    margin-right: 0;
+}
+
+/** Utility **/
+.right {
+    float: right;
+}
+.left {
+    float: left;
+}
+.clear {
+    clear: both;
+}
+.hidden {
+    display: none;
+}
+.text-right {
+    text-align: right;
+}
+.text-left {
+    text-align: left;
+}
+.text-center {
+    text-align: center;
+}
+
+/** CUSTOMIZATIONS **/
+form > div {
+    margin-bottom: 1rem;
+}
+.sidebar {
+    flex-basis: 100px;
+}
+.pane {
+    background-color: var(--page-bg);
+    padding: 1rem;
+    --aug-border-all: 1px;
+    --aug-border-bg: var(--border);
+}
+.nav {
+    margin-bottom: 1rem;
+}
+#main {
+    background-color: var(--page-bg);
+    padding: 1rem;
+    --aug-border-all: 1px;
+    --aug-border-bg: var(--border);
+}
+#corp-ads {
+    margin-top: 1rem;
+}
\ No newline at end of file
index 75fa346f9b26c7c5a6327646c204a00c93479423..e5e155758823d74a42fdd6275c768254ccaf6714 100644 (file)
@@ -64,33 +64,6 @@ server.post<{body: {username: string, password: string}}, void>('/login', async
 
 });
 
-server.get<{}, string>('/city', async req => {
-       const account = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
-       const city = await cityRepo.FindOne({ owner: account.id });
-
-
-       const buildQueues = await cityRepo.getBuildQueues(account.id);
-       const unitTrainingQueues = await cityRepo.getUnitTrainingQueues(account.id);
-
-       const buildings = await cityRepo.buildingRepository.list();
-       const units = await cityRepo.unitRepository.list();
-
-       let html = `
-       <h2>Kingom Overview</h2>
-       ${renderKingomOverview(city, account)}
-       <hr>
-       <h2>Land Development</h2>
-       ${renderLandDevelopment(city, buildings, buildQueues)}
-       <h2>Unit Training</h2>
-       ${renderUnitTraining(city, units, unitTrainingQueues)},
-       <h2>Map</h2>
-       ${renderOverworldMap(await cityRepo.FindAll(), city)}
-       <h2>Mail</h2>
-       ${renderMailroom(await mailRepo.listReceivedMessages(account.id))}
-       `;
-       return html;
-});
-
 server.post<{body: {
        soldiers: number,
        attackers: number,
@@ -125,7 +98,7 @@ server.get<{}, string>('/poll/overview', async req => {
        return renderKingomOverview(city, account);
 });
 
-server.get<{}, string>('/queue/construction', async req => {
+server.get<{}, string>('/poll/construction', async req => {
        const account = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
        const city = await cityRepo.FindOne({ owner: account.id });
        const buildings = await cityRepo.buildingRepository.list();
@@ -134,7 +107,7 @@ server.get<{}, string>('/queue/construction', async req => {
        return renderLandDevelopment(city, buildings, buildQueues);
 });
 
-server.get<{}, string>('/queue/units', async req => {
+server.get<{}, string>('/poll/unit-training', async req => {
        const account = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
        const city = await cityRepo.FindOne({ owner: account.id });
 
@@ -144,6 +117,19 @@ server.get<{}, string>('/queue/units', async req => {
        return renderUnitTraining(city, units, unitTrainingQueues);
 });
 
+server.get<{}, string>('/poll/map', async req => {
+       const account = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
+       const city = await cityRepo.FindOne({ owner: account.id });
+
+       return renderOverworldMap(await cityRepo.FindAll(), city);
+});
+
+server.get<{}, string>('/poll/mailroom', async req => {
+       const account = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
+
+       return renderMailroom(await mailRepo.listReceivedMessages(account.id));
+});
+
 
 server.post<{
        body: {
index 94d97917b96c386be3426df41b11d215b2bdbf2b..833341e17db62654aa698fc68257c9fad1b1ca6e 100644 (file)
@@ -36,6 +36,7 @@ export function renderOverworldMap(cities: City[], yourCity: City): string {
         rows[y] += '</tr>';
     }
     let html = `
+    <h2>Map</h2>
     <div id="city-offence"></div>
     <div id="outgoing-attacks" hx-trigger="reload-outgoing-attacks, every 600s, load" hx-get="/attacks/outgoing"></div>
     <table id="overworld-map">
index c44d8f852081950664658ee7a05c1fb58644c298..802eb010f239a379a2991391db3be9d226038a9a 100644 (file)
@@ -3,7 +3,9 @@ import { City } from "../repository/city";
 import * as _ from 'lodash';
 
 export function renderKingomOverview(city: City, account: Account): string {
-    return `<div hx-trigger="reload-construction-queue from:body, reload-unit-training from:body, every 600s" hx-get="/poll/overview">
+    return `
+       <div hx-trigger="every 600s" hx-get="/poll/overview">
+       <h2 data-augmented-ui="tl-clip bl-clip none">Kingdom Overview</h2>
        <table>
        <tr>
                <th>Lord</th>
@@ -46,5 +48,6 @@ export function renderKingomOverview(city: City, account: Account): string {
                <td>${city.bushels.toLocaleString()}</td>
        </tr>
        </table>
-       </div>`;
+       </div>
+       `;
 }
\ No newline at end of file
index 18b64a7f5f5a0562e8ed9704fcaa7eaae4152f5f..0f584427112e1dcafdf6581546461e1a5590fa7c 100644 (file)
@@ -6,8 +6,8 @@ import _ from "lodash";
 function progressBar(current, max): string {
     const percent = Math.ceil((current/max) * 100);
     return `
-    <div class="progress-bar construction" style="background: background: rgb(0,102,0);
-    background: linear-gradient(90deg, rgba(0,102,0,1) 0%, rgba(0,102,0,1) ${percent}%, rgba(0,0,0,1) ${percent}%);">
+    <div class="progress-bar construction" style="background: background: var(--green-bg);
+    background: linear-gradient(90deg, var(--green-bg) 0%, var(--green-bg) ${percent}%, #000 ${percent}%); border-color: var(--green-border); ">
     ${percent}%
     </div>
     `;
@@ -16,7 +16,8 @@ function progressBar(current, max): string {
 export function renderLandDevelopment(city: City, buildings: Building[], buildQueues: BuildQueue[]): string {
     const freeSpace = city.totalSpace - city.usedSpace;
     let html = `
-    <div hx-trigger="reload-construction-queue, every 600s" hx-get="/queue/construction">
+    <div hx-trigger="reload-construction-queue, every 600s" hx-get="/poll/construction">
+    <h2 data-augmented-ui="tl-clip bl-clip none">Construction</h2>
     <table>
     <tr>
         <th>Free Land</th>
@@ -31,7 +32,7 @@ export function renderLandDevelopment(city: City, buildings: Building[], buildQu
                 <td>${city[building.slug]}</td>
                 <td>
                     <form hx-post="/build">
-                        <input type="number" name="amount" hx-post="/cost/construction" hx-trigger="change" hx-target="#${building.slug}-cost">
+                        <input type="number" size="6" name="amount" hx-post="/cost/construction" hx-trigger="change" hx-target="#${building.slug}-cost">
                         <input type="hidden" name="building_type" value="${building.slug}">
                         <button type="submit">Build</button>
                     </form>
@@ -48,14 +49,13 @@ export function renderLandDevelopment(city: City, buildings: Building[], buildQu
 
     const queues = `
     <hr>
-    <h2>Build Queues</h2>
+    <h2 data-augmented-ui="tl-clip bl-clip none">Build Queues</h2>
     <table>
     <tr>
     <th>Building</th>
     <th>Amount Expected</th>
     <th>Progress</th>
     </tr>
-    <tr>
     ${buildQueues.sort((a, b) => {
         return a.due - b.due;
     }).map(queue => {
index 6ca831d7b68b3ea3c462872ebde813dac6b66842..7c20cc5d1a0657a416b02d4cfa8918bcbd53679f 100644 (file)
@@ -3,6 +3,8 @@ import { MessageWithNames } from "../repository/mail";
 
 export function renderMailroom(mail: MessageWithNames[]): string {
     return `
+    <div hx-trigger="every 600s" hx-get="/poll/mailroom">
+    <h2 data-augmented-ui="tl-clip bl-clip none">Mail</h2>
     <table>
     <tr>
         <th>From</th>
@@ -24,6 +26,7 @@ export function renderMailroom(mail: MessageWithNames[]): string {
     }).join("\n")}
     </table>
     <div id="individual-message"></div>
+    </div>
     `;
 }
 
@@ -48,6 +51,4 @@ export function renderMessage(msg: MessageWithNames): string {
     </tr>
     </table>
     `;
-    console.log(msg);
-    return '';
 }
\ No newline at end of file
index 85cf78d8c1a5ef00d1b75c738ea60d6ccf8eb6a1..dfbbb1bc40fee300ca5c66558f9e9059c8e65db0 100644 (file)
@@ -6,8 +6,8 @@ import { Unit } from "../repository/unit";
 function progressBar(current, max): string {
     const percent = Math.ceil((current/max) * 100);
     return `
-    <div class="progress-bar unit-training" style="background: background: rgb(0,102,0);
-    background: linear-gradient(90deg, rgba(0,102,0,1) 0%, rgba(0,102,0,1) ${percent}%, rgba(0,0,0,1) ${percent}%);">
+    <div class="progress-bar unit-training" style="background: background: var(--green-bg);
+    background: linear-gradient(90deg, var(--green-bg) 0%, var(--green-bg) ${percent}%, #000 ${percent}%); border-color: var(--green-border);">
     ${percent}%
     </div>
     `;
@@ -16,64 +16,70 @@ function progressBar(current, max): string {
 export function renderUnitTraining(city: City, units: Unit[], trainingQueues: UnitTrainingQueue[]): string {
     const unit = _.keyBy(units, 'slug');
     let html = `
-    <div hx-trigger="reload-unit-training, every 600s" hx-get="/queue/units">
+    <div hx-trigger="reload-unit-training, every 600s" hx-get="/poll/unit-training">
+    <h2 data-augmented-ui="tl-clip bl-clip none">Unit Training</h2>
     <table>
+    <tr>
+        <th>Unit</th>
+        <th>Avail.</td>
+        <th></th>
+    </tr>
     <tr>
         <th>Soldiers</th>
-        <td>${city.population} Avail.</td>
+        <td>${city.population}</td>
         <td>
             <form hx-post="/units">
-                <input type="number" name="amount" max="${city.population}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.soldiers.slug}-cost">
+                <input type="number" name="amount" size="6" max="${city.population}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.soldiers.slug}-cost">
                 <input type="hidden" name="type" value="${unit.soldiers.slug}">
-                <button type="submit" ${city.population ? '' : 'disabled'}>Train Units</button>
+                <button type="submit" ${city.population ? '' : 'disabled'}>Train</button>
             </form>
             <span id="${unit.soldiers.slug}-cost"></span>
         </td>
     </tr>
     <tr>
         <th>Attackers</th>
-        <td>${city.soldiers} Avail.</td>
+        <td>${city.soldiers}</td>
         <td>
             <form hx-post="/units">
-                <input type="number" name="amount" max="${city.soldiers}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.attackers.slug}-cost">
+                <input type="number" name="amount" size="6" max="${city.soldiers}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.attackers.slug}-cost">
                 <input type="hidden" name="type" value="${unit.attackers.slug}">
-                <button type="submit" ${city.soldiers ? '' : 'disabled'}>Train Units</button>
+                <button type="submit" ${city.soldiers ? '' : 'disabled'}>Train</button>
             </form>
             <span id="${unit.attackers.slug}-cost"></span>
         </td>
     </tr>
     <tr>
         <th>Defenders</th>
-        <td>${city.soldiers} Avail.</td>
+        <td>${city.soldiers}</td>
         <td>
             <form hx-post="/units">
-                <input type="number" name="amount" max="${city.soldiers}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.defenders.slug}-cost">
+                <input type="number" name="amount" size="6" max="${city.soldiers}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.defenders.slug}-cost">
                 <input type="hidden" name="type" value="${unit.defenders.slug}">
-                <button type="submit" ${city.soldiers ? '' : 'disabled'}>Train Units</button>
+                <button type="submit" ${city.soldiers ? '' : 'disabled'}>Train</button>
             </form>
             <span id="${unit.defenders.slug}-cost"></span>
         </td>
     </tr>
     <tr>
         <th>Special Attackers</th>
-        <td>${city.attackers} Avail.</td>
+        <td>${city.attackers}</td>
         <td>
             <form hx-post="/units">
-                <input type="number" name="amount" max="${city.attackers}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.sp_attackers.slug}-cost">
+                <input type="number" name="amount" size="6" max="${city.attackers}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.sp_attackers.slug}-cost">
                 <input type="hidden" name="type" value="${unit.sp_attackers.slug}">
-                <button type="submit" ${city.attackers ? '': 'disabled'}>Train Units</button>
+                <button type="submit" ${city.attackers ? '': 'disabled'}>Train</button>
             </form>
             <span id="${unit.sp_attackers.slug}-cost"></span>
         </td>
     </tr>
     <tr>
         <th>Special Defenders</th>
-        <td>${city.defenders} Avail.</td>
+        <td>${city.defenders}</td>
         <td>
             <form hx-post="/units">
-                <input type="number" name="amount" max="${city.defenders}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.sp_defenders.slug}-cost">
+                <input type="number" name="amount" size="6" max="${city.defenders}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.sp_defenders.slug}-cost">
                 <input type="hidden" name="type" value="${unit.sp_defenders.slug}">
-                <button type="submit" ${city.defenders ? '': 'disabled'}>Train Units</button>
+                <button type="submit" ${city.defenders ? '': 'disabled'}>Train</button>
             </form>
             <span id="${unit.sp_defenders.slug}-cost"></span>
         </td>
@@ -83,14 +89,13 @@ export function renderUnitTraining(city: City, units: Unit[], trainingQueues: Un
 
     const queues = `
     <hr>
-    <h2>Training Queues</h2>
+    <h2 data-augmented-ui="tl-clip bl-clip none">Training Queues</h2>
     <table>
     <tr>
     <th>Unit Type</th>
     <th>Amount Expected</th>
     <th>Progress</th>
     </tr>
-    <tr>
     ${trainingQueues.sort((a, b) => {
         return a.due - b.due;
     }).map(queue => {