visual-spending/styles.css
Anton Volnuhin 2d8cf00c3b Narrower donut container and safe-area side padding for landscape phones
- Chart wrapper 52% (was 58%) for visual separation from details box
- Container padding 20px + safe-area-inset on both sides to clear
  Dynamic Island in landscape
- Details box right offset includes safe-area-inset-right

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 23:42:07 +03:00

1088 lines
20 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "SF Pro", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: #f5f5f5;
color: #333;
}
.container {
width: 100%;
margin: 0 auto;
padding: 20px;
padding-top: calc(20px + env(safe-area-inset-top));
}
.header {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 20px;
}
/* Month Navigator */
.month-navigator {
display: flex;
align-items: center;
gap: 8px;
max-width: 60%;
margin-left: auto;
}
.nav-arrow {
width: 36px;
height: 36px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: white;
font-size: 24px;
line-height: 1;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.2s, border-color 0.2s;
flex-shrink: 0;
}
.nav-arrow:hover:not(:disabled) {
background-color: #f0f0f0;
border-color: #999;
}
.nav-arrow:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.month-list {
display: flex;
gap: 6px;
overflow-x: auto;
scrollbar-width: none;
-ms-overflow-style: none;
}
.month-list::-webkit-scrollbar {
display: none;
}
.month-btn {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: white;
font-size: 14px;
cursor: pointer;
white-space: nowrap;
transition: background-color 0.2s, border-color 0.2s, color 0.2s;
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
}
.month-btn:hover {
background-color: #f0f0f0;
border-color: #999;
}
.month-btn.active {
background-color: #4a4a4a;
border-color: #4a4a4a;
color: white;
}
.month-preview {
width: 40px;
height: 40px;
border-radius: 50%;
border: 1px solid #eee;
position: relative;
}
.month-preview::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 35%;
height: 35%;
background: white;
border-radius: 50%;
}
.month-btn.active .month-preview {
border-color: rgba(255, 255, 255, 0.3);
}
.month-btn.active .month-preview::after {
background: #4a4a4a;
}
.month-label {
font-size: 12px;
}
/* View switcher */
.view-switcher {
display: inline-flex;
gap: 4px;
}
.view-switcher-btn {
background: none;
border: none;
border-bottom: 2px solid transparent;
padding: 4px 8px;
font-size: 14px;
font-family: inherit;
color: #999;
cursor: pointer;
transition: color 0.2s, border-color 0.2s;
}
.view-switcher-btn:hover {
color: #666;
}
.view-switcher-btn.active {
color: #333;
border-bottom-color: #333;
}
/* Timeline mode modifiers */
.container.timeline-mode #center-label,
.container.timeline-mode #chart-eye-btn,
.container.timeline-mode #details-box {
display: none !important;
}
.container.timeline-mode .month-navigator {
display: none;
}
.container.timeline-mode .chart-wrapper {
width: 100%;
}
.content-wrapper {
display: flex;
position: relative;
}
.chart-wrapper {
position: relative;
width: 70%;
height: 80vh;
}
#chart-container {
width: 100%;
height: 100%;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
/* Center label overlay */
.center-label {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
text-align: left;
pointer-events: none;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro", "Segoe UI", system-ui, sans-serif;
z-index: 10;
}
.center-month {
font-size: 18px;
font-weight: 400;
color: #666;
line-height: 1.0;
}
.center-category {
font-size: 18px;
font-weight: 600;
color: #333;
line-height: 1.0;
}
.center-category:empty {
display: none;
}
.center-home {
display: none;
justify-content: center;
pointer-events: auto;
cursor: pointer;
}
.center-home svg {
width: 32px;
height: 32px;
color: #333;
background: rgba(255, 255, 255, 0.9);
border-radius: 50%;
padding: 8px;
transition: all 0.2s;
}
.center-home:hover svg {
color: #000;
background: #fff;
}
.center-amount {
font-size: 22px;
font-weight: 700;
line-height: 1.0;
}
.center-amount-num {
color: #000;
}
.center-rub {
color: #888;
font-size: 20px;
}
/* Override ECharts default pointer cursor - only eye button should have pointer */
#chart-container canvas {
cursor: default !important;
}
#details-box {
position: absolute;
top: 10px;
right: 10px;
width: 35vw;
min-width: 300px;
max-width: 650px;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.15);
padding: 20px;
z-index: 10;
min-height: auto;
max-height: 80vh;
overflow-y: auto;
opacity: 0.98;
border: 1px solid #eee;
margin-left: 0;
}
#details-box h3 {
margin-bottom: 15px;
color: #333;
font-size: 18px;
border-bottom: 1px solid #eee;
padding-bottom: 8px;
}
#details-box h4 {
margin: 18px 0 5px;
color: #555;
font-size: 14px;
}
.details-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding: 10px 0;
border-bottom: 1px solid #eee;
}
.hover-name {
font-weight: bold;
color: #333;
word-break: break-word;
flex: 1;
display: flex;
align-items: center;
font-size: 20px;
}
.hover-amount {
font-size: 20px;
color: #0066cc;
font-weight: bold;
margin-left: 10px;
white-space: nowrap;
text-align: right;
min-width: 120px;
}
.color-circle {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 8px;
flex-shrink: 0;
}
#top-items {
font-size: 14px;
}
.top-item {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px dashed #eee;
}
.top-item:last-child {
border-bottom: none;
}
.top-item-name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: flex;
align-items: center;
min-width: 0;
width: 50%;
flex-shrink: 0;
}
.top-item-amount {
margin-left: auto;
font-weight: bold;
min-width: 100px;
text-align: right;
flex-shrink: 0;
}
.top-item-percent {
margin-left: 8px;
width: 45px;
text-align: right;
flex-shrink: 0;
color: #999;
font-weight: 400;
}
/* Add responsive design for smaller screens */
@media (max-width: 1200px) {
.chart-wrapper {
width: 60%;
}
#details-box {
width: 38%;
min-width: 340px;
}
}
@media (max-width: 850px) {
.view-switcher {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 900;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
display: flex;
justify-content: center;
gap: 0;
padding: 8px 16px;
padding-bottom: calc(8px + env(safe-area-inset-bottom));
border-top: 1px solid #e0e0e0;
}
.view-switcher-btn {
flex: 1;
max-width: 160px;
padding: 8px 16px;
border-radius: 20px;
border-bottom: none;
font-size: 14px;
font-weight: 500;
text-align: center;
color: #666;
background: transparent;
}
.view-switcher-btn.active {
background: #333;
color: #fff;
border-bottom: none;
}
.container {
padding-bottom: calc(70px + env(safe-area-inset-bottom));
}
.header {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 15px;
}
.month-navigator {
max-width: 100%;
width: 100%;
}
.content-wrapper {
flex-direction: column;
}
.chart-wrapper {
width: 100%;
height: min(90lvw, calc(100dvh - 130px));
}
#details-box {
position: relative;
top: 0;
right: 0;
width: 100%;
margin-top: 15px;
max-height: calc(100vh - 90vw - 130px);
min-width: 100%;
overflow-y: auto;
}
.container.timeline-mode .chart-wrapper {
width: 100%;
height: calc(100dvh - 130px - env(safe-area-inset-bottom));
}
.top-item-amount {
min-width: 80px;
}
.center-label {
display: none;
}
/* Still show home icon on narrow screens when drilled down */
.center-label.drilled-down {
display: flex;
align-items: center;
justify-content: center;
}
.center-label.drilled-down .center-home {
display: flex;
}
.center-label.drilled-down .center-month,
.center-label.drilled-down .center-category,
.center-label.drilled-down .center-amount {
display: none;
}
.chart-eye-btn {
display: none !important;
}
}
@media (max-width: 500px) {
h1 {
font-size: 22px;
}
.month-btn {
padding: 4px 6px;
font-size: 11px;
}
.month-preview {
width: 28px;
height: 28px;
}
.month-label {
font-size: 9px;
}
.nav-arrow {
width: 28px;
height: 28px;
font-size: 18px;
}
.month-navigator {
gap: 4px;
}
.month-list {
gap: 4px;
}
.container {
padding: 15px 10px;
}
.chart-wrapper {
height: min(95vw, calc(100dvh - 130px));
}
#chart-container {
background-color: transparent;
box-shadow: none;
border-radius: 0;
}
}
@media (max-width: 400px) {
h1 {
font-size: 20px;
}
.container {
padding: 12px 8px;
}
.month-btn {
padding: 4px 5px;
}
.month-preview {
width: 26px;
height: 26px;
}
.nav-arrow {
width: 26px;
height: 26px;
font-size: 16px;
}
.chart-wrapper {
height: min(100vw, calc(100dvh - 130px));
}
.details-header {
padding: 8px 0;
}
.hover-name {
font-size: 16px;
}
.hover-amount {
font-size: 16px;
min-width: 100px;
}
#details-box h4 {
margin: 12px 0 5px;
font-size: 13px;
}
.top-item {
padding: 6px 0;
}
.top-item-name {
font-size: 13px;
}
.top-item-amount {
font-size: 13px;
min-width: 70px;
}
.top-item-percent {
font-size: 12px;
width: 40px;
}
}
/* Landscape phones: revert to desktop-like layout */
@media (max-width: 850px) and (orientation: landscape) {
h1 {
font-size: 20px;
white-space: nowrap;
}
.view-switcher {
position: static;
background: none;
backdrop-filter: none;
-webkit-backdrop-filter: none;
padding: 0;
border-top: none;
display: inline-flex;
gap: 4px;
flex-shrink: 0;
}
.view-switcher-btn {
flex: none;
max-width: none;
padding: 4px 8px;
border-radius: 0;
border-bottom: 2px solid transparent;
font-size: 13px;
font-weight: normal;
color: #999;
background: none;
}
.view-switcher-btn.active {
color: #333;
background: none;
border-bottom-color: #333;
}
.container {
padding: 4px 10px;
padding-top: calc(4px + env(safe-area-inset-top));
padding-bottom: 4px;
padding-left: calc(10px + env(safe-area-inset-left));
padding-right: calc(10px + env(safe-area-inset-right));
}
.header {
flex-direction: row;
align-items: center;
gap: 8px;
margin-bottom: 2px;
}
.month-navigator {
max-width: 55%;
width: auto;
flex-shrink: 1;
min-width: 0;
}
.month-btn {
padding: 2px 4px;
}
.month-preview {
width: 22px;
height: 22px;
}
.month-label {
font-size: 8px;
}
.nav-arrow {
width: 24px;
height: 24px;
font-size: 16px;
}
.content-wrapper {
flex-direction: row;
}
.chart-wrapper {
width: 55%;
height: calc(100dvh - 38px);
}
#details-box {
position: relative;
top: 0;
right: 0;
width: 45%;
min-width: auto;
margin-top: 0;
max-height: calc(100dvh - 38px);
overflow-y: auto;
padding: 10px;
}
.container.timeline-mode .chart-wrapper {
width: 100%;
height: calc(100dvh - 38px);
}
.details-header {
padding: 6px 0;
}
.top-item {
padding: 4px 0;
}
}
/* Larger landscape phones (e.g. iPhone Pro Max ~956×440): compact center, tighter details */
@media (min-width: 851px) and (max-height: 500px) and (orientation: landscape) {
.center-label {
display: none;
}
.center-label.drilled-down {
display: flex;
align-items: center;
justify-content: center;
}
.center-label.drilled-down .center-home {
display: flex;
}
.center-label.drilled-down .center-month,
.center-label.drilled-down .center-category,
.center-label.drilled-down .center-amount {
display: none;
}
.container {
padding-left: calc(20px + env(safe-area-inset-left));
padding-right: calc(20px + env(safe-area-inset-right));
}
.chart-wrapper {
width: 52%;
height: calc(100dvh - 50px);
}
#details-box {
width: 40vw;
padding: 10px;
max-height: calc(100dvh - 50px);
right: calc(4px + env(safe-area-inset-right));
}
.details-header {
margin-bottom: 6px;
padding: 4px 0;
}
.hover-name {
font-size: 13px;
}
.hover-amount {
font-size: 16px;
}
#details-box h4 {
margin: 6px 0 2px;
font-size: 11px;
}
.top-item {
padding: 3px 0;
font-size: 12px;
}
.top-item-amount {
font-size: 12px;
}
}
/* Eye button for transaction details */
.eye-btn {
opacity: 0;
background: none;
border: none;
cursor: pointer;
padding: 2px 6px;
margin-left: 4px;
transition: opacity 0.2s;
flex-shrink: 0;
color: #666;
display: flex;
align-items: center;
}
.top-item:hover .eye-btn {
opacity: 0.5;
}
.eye-btn:hover {
opacity: 1 !important;
color: #333;
}
/* Header eye button - show on hover like other items */
.details-header:hover .header-eye-btn {
opacity: 0.5;
}
/* Always show eye buttons on touch/narrow devices (no hover) */
@media (max-width: 850px) {
.eye-btn,
.header-eye-btn {
opacity: 0.5;
}
}
/* Floating eye button on chart (shown when hovering over sunburst sector) */
.chart-eye-btn {
position: absolute;
z-index: 100;
background: transparent;
border: none;
border-radius: 50%;
width: 24px;
height: 24px;
cursor: pointer !important;
display: flex;
align-items: center;
justify-content: center;
filter: drop-shadow(0 0 2px rgba(255, 255, 255, 1)) drop-shadow(0 0 3px rgba(255, 255, 255, 1)) drop-shadow(0 0 5px rgba(255, 255, 255, 0.9));
transition: filter 0.15s, opacity 0.2s ease-out;
color: #111;
pointer-events: none;
opacity: 0;
}
.chart-eye-btn:hover {
filter: drop-shadow(0 0 2px rgba(255, 255, 255, 1)) drop-shadow(0 0 4px rgba(255, 255, 255, 1)) drop-shadow(0 0 6px rgba(255, 255, 255, 1));
color: #000;
}
.chart-eye-btn svg {
width: 14px;
height: 14px;
}
/* Modal styles */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
width: 90vw;
max-width: 1200px;
height: 80vh;
background-color: white;
border-radius: 12px;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3);
display: flex;
flex-direction: column;
overflow: hidden;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
border-bottom: 1px solid #eee;
flex-shrink: 0;
}
.modal-header h2 {
margin: 0;
font-size: 18px;
color: #333;
}
.modal-close {
background: none;
border: none;
font-size: 28px;
cursor: pointer;
color: #666;
line-height: 1;
padding: 0 4px;
}
.modal-close:hover {
color: #333;
}
.modal-body {
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
}
.modal-table-container {
flex: 1;
overflow: scroll;
padding: 0;
}
.transaction-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.transaction-table th,
.transaction-table td {
padding: 10px 12px;
text-align: left;
border-bottom: 1px solid #eee;
white-space: nowrap;
max-width: 250px;
overflow: hidden;
text-overflow: ellipsis;
}
.transaction-table th {
background-color: #f8f9fa;
font-weight: 600;
color: #555;
position: sticky;
top: 0;
z-index: 1;
}
.transaction-table tbody tr:hover {
background-color: #f5f8ff;
}
.transaction-table tbody tr:last-child td {
border-bottom: none;
}
/* Mobile modal styles */
@media (max-width: 850px) {
.modal-content {
width: 95vw;
height: 90vh;
}
.transaction-table {
font-size: 11px;
}
.transaction-table th,
.transaction-table td {
padding: 8px 10px;
max-width: 150px;
}
}
/* Row detail modal styles */
.row-detail-content {
position: relative;
width: 90vw;
max-width: 500px;
max-height: 80vh;
background-color: white;
border-radius: 12px;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3);
padding: 24px;
overflow-y: auto;
}
.row-detail-content .modal-close {
position: absolute;
top: 12px;
right: 12px;
}
.row-detail-content h3 {
margin: 0 0 20px 0;
font-size: 18px;
color: #333;
padding-right: 30px;
}
#row-detail-body {
display: table;
width: 100%;
border-collapse: collapse;
}
.row-detail-item {
display: table-row;
}
.row-detail-item:hover {
background-color: #f8f9fa;
}
.row-detail-label {
display: table-cell;
padding: 8px 32px 8px 0;
font-size: 13px;
color: #999;
font-weight: 400;
white-space: nowrap;
vertical-align: top;
border-bottom: 1px solid #eee;
min-width: 140px;
}
.row-detail-value {
display: table-cell;
padding: 8px 0;
font-size: 13px;
color: #222;
font-weight: 600;
word-break: break-word;
border-bottom: 1px solid #eee;
}
.row-detail-item:last-child .row-detail-label,
.row-detail-item:last-child .row-detail-value {
border-bottom: none;
}
/* Sortable table header styles */
.transaction-table th {
cursor: pointer;
user-select: none;
position: relative;
padding-right: 20px !important;
}
.transaction-table th:hover {
background-color: #f0f0f0;
}
.transaction-table th::after {
content: '';
position: absolute;
right: 6px;
top: 50%;
transform: translateY(-50%);
border: 4px solid transparent;
opacity: 0.3;
}
.transaction-table th.sort-asc::after {
border-bottom-color: #333;
border-top: none;
opacity: 1;
}
.transaction-table th.sort-desc::after {
border-top-color: #333;
border-bottom: none;
opacity: 1;
}
.transaction-table tbody tr {
cursor: pointer;
transition: background-color 0.15s;
}