:root {
--control-bar-height: 44px;
}
/* html (not body) is the actual scrolling element on desktop, so the
reserved space for the fixed control bar belongs on it: that's the
ancestor position:sticky measures its offsets against. */
html {
padding-bottom: var(--control-bar-height);
}
body {
font-family: 'Helvetica Neue', Verdana, Helvetica, Arial, sans-serif;
max-width: 600px;
margin: 0;
-webkit-font-smoothing: antialiased;
font-size: 1.125em;
color: #333;
line-height: 1.5em;
}
/* On narrow screens, contain scroll to the sheet area so position:fixed
stays anchored to the viewport rather than the document bottom. */
@media (max-width: 767px) {
html, body {
height: 100%;
overflow: hidden;
padding-bottom: 0; /* .sheet-container becomes the scrolling ancestor instead; see below */
}
#app {
height: 100%;
display: flex;
flex-direction: column;
}
#main-layout {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
}
.sheet-container {
flex: 1;
overflow: auto;
min-height: 0;
padding-bottom: var(--control-bar-height);
}
}
h1, h2, h3 {
color: #000;
}
h1 {
font-size: 2.5em
}
h2 {
font-size: 2em
}
h3 {
font-size: 1.5em
}
a {
text-decoration: none;
color: #09f;
}
a:hover {
text-decoration: underline;
}
#main-table {
/*position: fixed;
top: 0;
left: 0;*/
}
table {
border-collapse: separate;
border-spacing: 0;
border-top: 1px solid black;
border-left: 1px solid black;
}
td, th {
border-right: 1px solid black;
border-bottom: 1px solid black;
padding: 0;
}
/* Sticky column headers (top row) */
#main-table tr:first-child th {
position: sticky;
top: 0;
z-index: 1;
background: white;
}
/* Sticky row headers (left column) */
#main-table tr:not(:first-child) th {
position: sticky;
left: 0;
z-index: 1;
background: white;
}
/* Corner cell: sticky in both directions */
#main-table tr:first-child th:first-child {
left: 0;
z-index: 2;
}
/* Totals row: pinned to the bottom of the viewport, just above the control bar.
bottom:0 rests it flush against the scrolling ancestor's own reserved
padding-bottom (html on desktop, .sheet-container on mobile) - deliberately
not an explicit --control-bar-height offset here, so the space is only
ever accounted for once. */
#main-table .totals-row th,
#main-table .totals-row td {
position: sticky;
bottom: 0;
z-index: 1;
background: #e8edf5;
border-top: 2px solid #333;
}
/* Bottom-left corner: sticky in both directions, like the top-left corner cell */
#main-table .totals-row th:first-child {
z-index: 2;
}
.totals-row td {
padding: 5px;
text-align: right;
font-weight: 600;
}
.totals-row .totals-error {
color: #b00020;
}
/* Totals row header cell: click to cycle sum / average / both */
#main-table .totals-row th.totals-toggle {
cursor: pointer;
}
#main-table .totals-row th.totals-toggle:hover {
background: #dde5f0;
}
/* "Both" mode: sum and average stacked in smaller font */
.totals-row--both td {
font-size: 0.75em;
}
.totals-row--both .totals-value {
line-height: 1.3;
}
th {
text-align: center;
min-width: 40px;
}
td {
text-align: right;
position: relative;
}
td input.highlighted {
background-color: lightblue;
}
td input {
border: none;
padding: 5px;
width: 80px;
}
td input:hover {
background-color: #ccc;
}
td input:focus {
background-color: #ccf;
}
td input:not(:focus) {
text-align: right;
}
/* ── Control bar ── */
#control-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: var(--control-bar-height);
display: flex;
align-items: center;
background: #f5f5f5;
border-top: 1px solid #ccc;
z-index: 999;
}
#bar-logo {
height: var(--control-bar-height);
width: 44px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
border-right: 1px solid #ccc;
}
#bar-logo img {
width: 100%;
height: 100%;
}
.bar-btn {
height: 100%;
padding: 0 16px;
border: none;
border-right: 1px solid #ccc;
background: none;
cursor: pointer;
font-size: 0.9em;
color: #333;
}
.bar-btn:hover {
background: #e8e8e8;
}
/* ── About modal ── */
#about-modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.45);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
padding: 16px;
}
#about-modal {
background: white;
border-radius: 8px;
padding: 24px;
max-width: 480px;
width: 100%;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 4px 24px rgba(0,0,0,0.2);
}
#about-modal button {
margin-top: 12px;
padding: 8px 20px;
border: 1px solid #ccc;
border-radius: 4px;
background: #f5f5f5;
cursor: pointer;
}
#about-modal button:hover {
background: #e0e0e0;
}
#active-table-name {
padding: 0 12px;
font-size: 0.85em;
color: #555;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 140px;
}
/* ── Table modal (todo 11.1) ── */
#table-modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.45);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
padding: 16px;
}
#table-modal {
background: white;
border-radius: 8px;
padding: 24px;
max-width: 480px;
width: 100%;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 4px 24px rgba(0,0,0,0.2);
}
.table-modal-tabs {
display: flex;
border-bottom: 1px solid #e0e0e0;
margin-bottom: 12px;
}
.table-tab {
padding: 8px 16px;
border: none;
background: none;
cursor: pointer;
font-size: 0.95em;
color: #666;
border-bottom: 2px solid transparent;
}
.table-tab--active {
color: #333;
border-bottom-color: #09f;
font-weight: 600;
}
.table-modal-empty {
color: #777;
}
.table-list {
display: flex;
flex-direction: column;
gap: 4px;
}
.table-row {
display: flex;
align-items: center;
justify-content: space-between;
border: 1px solid #eee;
border-radius: 6px;
padding: 8px 10px;
}
.table-row--active {
background: #eef6ff;
border-color: #bcdcff;
}
.table-row-main {
flex: 1;
cursor: pointer;
min-width: 0;
}
.table-row-name {
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.table-row-meta {
font-size: 0.75em;
color: #888;
}
.table-row-menu-wrap {
position: relative;
flex-shrink: 0;
}
.icon-btn {
border: none;
background: none;
cursor: pointer;
font-size: 1.1em;
padding: 4px 8px;
color: #555;
}
.icon-btn:hover {
background: #eee;
border-radius: 4px;
}
.table-row-menu {
position: absolute;
right: 0;
top: 100%;
background: white;
border: 1px solid #ccc;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
display: flex;
flex-direction: column;
z-index: 1001;
min-width: 100px;
}
.table-row-menu button {
border: none;
background: none;
text-align: left;
padding: 8px 12px;
cursor: pointer;
}
.table-row-menu button:hover {
background: #f0f0f0;
}
.table-row-edit {
display: flex;
align-items: center;
gap: 4px;
width: 100%;
}
.table-row-edit input {
flex: 1;
padding: 6px 8px;
border: 1px solid #ccc;
border-radius: 4px;
}
.table-modal-help p {
margin: 0 0 12px;
}
.table-modal-close {
margin-top: 16px;
padding: 8px 20px;
border: 1px solid #ccc;
border-radius: 4px;
background: #f5f5f5;
cursor: pointer;
}
.table-modal-close:hover {
background: #e0e0e0;
}
/* ── Delete confirmation modal ── */
#table-delete-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.55);
z-index: 1100;
display: flex;
align-items: center;
justify-content: center;
padding: 16px;
}
#table-delete-modal {
background: white;
border-radius: 8px;
padding: 20px;
max-width: 360px;
width: 100%;
box-shadow: 0 4px 24px rgba(0,0,0,0.25);
}
#table-delete-modal button {
margin-top: 12px;
margin-right: 8px;
padding: 8px 16px;
border: 1px solid #ccc;
border-radius: 4px;
background: #f5f5f5;
cursor: pointer;
}
#table-delete-modal button.danger {
background: #d32f2f;
color: white;
border-color: #d32f2f;
}
#table-delete-modal button:hover {
filter: brightness(0.95);
}
.smartborder {
width: calc(100% + 2px);
height: calc(100% + 2px);
border: 3px solid blue;
position: absolute;
left: -4px;
top: -4px;
background: none;
z-index: 999;
/*pointer-events: none;*/
}
td input:not(:focus) + .smartborder {
visibility: hidden;
}
.smartborder .button {
--button-size: 20px;
--half-button-size: 10px;
/*pointer-events: all;/* TODO check this (what is "initial"?) */
width: var(--button-size);
height: var(--button-size);
background-color: blue;
color: white;
border-radius: var(--half-button-size);
position: absolute;
}
.extendrange.button {
left: 100%;
top: 100%;
}
.fillrange.button {
top: 100%;
left: calc(50% - var(--half-button-size));
}
.moverange.button {
top: calc(50% - var(--half-button-size));
left: calc(0px - var(--button-size));
}
.emptyrange.button {
top: calc(0px - var(--button-size));
left: calc(100% - var(--button-size) - 5px);
}
.deleterange.button {
top: calc(0px - var(--button-size));
left: 100%;
}
.copyrange.button {
top: calc(0px - var(--button-size));
left: 0;
}