diff --git a/app/static/css/auth.css b/app/static/css/auth.css index 70da6cd..31081bb 100644 --- a/app/static/css/auth.css +++ b/app/static/css/auth.css @@ -130,3 +130,20 @@ color: var(--accent) !important; border-color: var(--accent) !important; } + + +/* --- Mobile (≤480px) -------------------------------------------------- */ + +@media (max-width: 480px) { + /* The card is already width:360px;max-width:100% so it fills the + screen — just tighten internal padding to free up vertical space + for the keyboard on iOS Safari (which eats half the viewport). */ + .auth-card { padding: 20px 18px; } + .auth-card__brand { font-size: 14px; } + .auth-card__lede { font-size: 12px; } + .auth-card input, + .auth-card button[type="submit"] { + font-size: 14px; /* avoids iOS Safari zoom-on-focus */ + padding: 10px 12px; + } +} diff --git a/app/static/css/dashboard.css b/app/static/css/dashboard.css index 956157d..aae9b6a 100644 --- a/app/static/css/dashboard.css +++ b/app/static/css/dashboard.css @@ -226,3 +226,42 @@ vertical-align: middle; user-select: none; } + + +/* --- Mobile (≤480px) -------------------------------------------------- */ + +@media (max-width: 480px) { + /* Hide secondary indicator-table columns: Label, Ccy, 1y, anchor, + as-of. The cells are tagged with .mobile-hide in indicators.html; + this rule keeps display intent in CSS while letting the template + handle the conditional anchor column. Symbol / Price / 1d / 1m + remain — the four numbers a phone user actually wants. */ + .dense .mobile-hide { display: none; } + + /* Tighter cell padding so the four remaining columns fit + comfortably on a 360px viewport. */ + .dense th, .dense td { + padding: 4px 6px; + font-size: 11px; + } + /* Symbol column gets a touch more breathing room — it's the + identifying anchor. */ + .dense td.label { font-weight: 600; } + + /* Group-tabs strip already has overflow-x:auto; widen the tap + targets so swipe-scrolling on a touchscreen feels natural. */ + .group-tabs button { + padding: 8px 14px; + font-size: 11.5px; + white-space: nowrap; + } + + /* Aggregate-read summary header tightens — stack the label above + the timestamp to avoid wrapping at awkward points. */ + .ind-summary__head { + flex-direction: column; + align-items: flex-start; + gap: 2px; + } + .ind-summary__body { font-size: 12px; } +} diff --git a/app/static/css/log-chat.css b/app/static/css/log-chat.css index 6e847ec..895953b 100644 --- a/app/static/css/log-chat.css +++ b/app/static/css/log-chat.css @@ -280,3 +280,46 @@ } .chat-form button:hover:not(:disabled) { background: var(--accent); color: var(--bg); } .chat-form button:disabled { opacity: 0.4; cursor: not-allowed; } + + +/* --- Mobile (≤480px) -------------------------------------------------- */ + +@media (max-width: 480px) { + /* Trim horizontal padding so the markdown column uses the screen + width. The existing 1100px rule already capped the column at + 76ch; we just shave the surrounding gutter. */ + .log-content { padding: 0 4px; font-size: 13.5px; } + .log-content h2 { font-size: 16px; } + .log-content h3 { font-size: 14px; } + + /* Chat bubbles edge-to-edge so the conversation reads like a + mobile messenger thread. */ + .chat-msg { + max-width: 100%; + padding: 8px 10px; + font-size: 13px; + } + .chat-msg--user { margin-right: 0; } + .chat-msg--assistant { margin-left: 0; } + + /* Chat input row stacks: textarea full-width, button below. */ + .chat-form { + flex-direction: column; + gap: 6px; + padding: 8px; + } + .chat-form textarea { + width: 100%; + min-height: 56px; + font-size: 14px; /* avoids iOS Safari zoom-on-focus */ + } + .chat-form button { + width: 100%; + padding: 10px; + font-size: 12px; + } + + .chat-header { padding: 8px 10px; } + .chat-title { font-size: 12px; } + .chat-hint { font-size: 10px; } +} diff --git a/app/static/css/news.css b/app/static/css/news.css index 8827339..3fb4bc6 100644 --- a/app/static/css/news.css +++ b/app/static/css/news.css @@ -84,3 +84,40 @@ } .news-tag--clear { color: var(--dim); border-style: dashed; } .news-tag--clear:hover { color: var(--negative); border-color: var(--negative); } + + +/* --- Mobile (≤480px) -------------------------------------------------- */ + +@media (max-width: 480px) { + /* The 720px rule already collapsed to age | source | title and + hid the right-side tag chips. At ≤480 we drop the source column + too and let the title flow under the age, with source as a small + line below the title — saves another ~100px of horizontal room. */ + .news-row { + grid-template-columns: 50px minmax(0, 1fr); + gap: 8px; + padding: 6px 10px; + } + .news-row .source { + grid-column: 2; + grid-row: 2; + font-size: 10.5px; + } + .news-row .title { + grid-column: 2; + grid-row: 1; + font-size: 12.5px; + line-height: 1.35; + } + + /* Tag filter strip wraps onto multiple rows on a phone. */ + .news-tags { + flex-wrap: wrap; + gap: 6px; + padding: 6px 8px; + } + .news-tag { + padding: 4px 8px; + font-size: 11px; + } +} diff --git a/app/static/css/panels.css b/app/static/css/panels.css index 18293a5..4d2d9a3 100644 --- a/app/static/css/panels.css +++ b/app/static/css/panels.css @@ -90,3 +90,19 @@ table.dense tr:hover td { background: color-mix(in srgb, var(--accent) 5%, trans transition: opacity 0.2s; } .htmx-request .htmx-indicator { opacity: 1; } + + +/* --- Mobile (≤480px) -------------------------------------------------- */ + +@media (max-width: 480px) { + .panel-header { + padding: 8px 10px; + gap: 8px; + } + .panel-header .title { font-size: 12px; } + .panel-header .meta { font-size: 10px; } + .panel-body { padding: 4px 6px; } + /* Scroll panels lose some vertical room on small screens so the + stacked layout doesn't push log/news off the fold. */ + .panel-body--scroll { max-height: 60vh; } +} diff --git a/app/static/css/portfolio.css b/app/static/css/portfolio.css index 89ffb32..cdf0417 100644 --- a/app/static/css/portfolio.css +++ b/app/static/css/portfolio.css @@ -374,3 +374,33 @@ details[open] .pf-analysis__head-left::before { content: "▾ "; } color: var(--muted); background: color-mix(in srgb, var(--accent) 4%, transparent); } + + +/* --- Mobile (≤480px) -------------------------------------------------- */ + +@media (max-width: 480px) { + /* The existing 640px breakpoint already moves the overall grid to + 2 cols. At ≤480 we keep 2 cols but tighten gap so the stat + values don't crowd the labels next to them. */ + .pf-overall__grid { gap: 4px 12px; } + .pf-stat-value { font-size: 14px; } + + /* Action buttons wrap to multiple rows instead of squishing onto + one. flex-wrap was already set above; ensure each button has a + comfortable tap target. */ + .pf-actions { flex-wrap: wrap; gap: 6px; } + .pf-actions button { + flex: 1 1 auto; + padding: 8px 12px; + font-size: 11.5px; + } + + /* Pill row stays wrapped; just give pills a small min-width so + two-character tags (USD, EUR) don't hug each other awkwardly. */ + .pf-pill { padding: 3px 7px; } + + /* The inline composer's input gets the full width — the desktop's + intrinsic-width sizing leaves it tiny on a phone. */ + .pf-add__line { flex-wrap: wrap; gap: 6px; } + .pf-add__line input, .pf-add__line textarea { width: 100%; } +} diff --git a/app/static/css/public.css b/app/static/css/public.css index 0e0e361..9b1f753 100644 --- a/app/static/css/public.css +++ b/app/static/css/public.css @@ -715,3 +715,29 @@ a.btn-secondary:hover { color: var(--accent); border-color: var(--accent); } color: var(--accent); outline: none; } + + +/* --- Mobile (≤480px) -------------------------------------------------- */ + +@media (max-width: 480px) { + /* Hero headline already uses clamp(); shrink its lower bound so a + two-line headline doesn't push the CTAs below the fold on a + 360px screen. */ + .hero__headline { font-size: clamp(22px, 6vw, 32px); } + .hero__subhead { font-size: 14px; } + + /* CTAs stack full-width on phones — easier tap targets. */ + .hero__ctas { flex-direction: column; align-items: stretch; } + .btn-primary, .btn-secondary { + width: 100%; + text-align: center; + padding: 12px 18px; + } + + /* Tier cards (pricing page) stack on phones. */ + .tier-grid { grid-template-columns: 1fr; gap: 16px; } + .tier-card { padding: 18px; } + + /* Tighten public-page outer padding. */ + .public-shell { padding: 16px 12px; } +} diff --git a/app/static/css/settings.css b/app/static/css/settings.css index 1d6c0e5..819d69b 100644 --- a/app/static/css/settings.css +++ b/app/static/css/settings.css @@ -379,3 +379,38 @@ box-sizing: border-box; } .modal-input:focus { border-color: var(--accent); } + + +/* --- Mobile (≤480px) -------------------------------------------------- */ + +@media (max-width: 480px) { + /* Form rows stack: label above value instead of side-by-side. The + desktop layout uses a fixed 110px label column that pinches the + value column unbearably on a phone. */ + .settings-row { + flex-direction: column; + align-items: stretch; + gap: 4px; + padding: 10px 0; + } + .settings-row__label { + width: auto; + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.06em; + } + .settings-select { + width: 100%; + font-size: 14px; /* avoids iOS Safari zoom-on-focus */ + } + + /* The two-column import picker becomes single column. */ + .import-choice { + flex: 1 1 100%; + min-width: 0; + } + + /* Buttons get a full-width tap target. */ + .settings-btn { width: 100%; padding: 10px; } + .settings-icon-btn { width: 100%; justify-content: center; } +} diff --git a/app/templates/partials/indicators.html b/app/templates/partials/indicators.html index 0ae1e1f..70aaab0 100644 --- a/app/templates/partials/indicators.html +++ b/app/templates/partials/indicators.html @@ -20,11 +20,11 @@
| Symbol | Label | -Price | Ccy | -1d | 1m | 1y | - {% if has_anchor %}anchor | {% endif %} -as-of | +Symbol | Label | +Price | Ccy | +1d | 1m | 1y | + {% if has_anchor %}anchor | {% endif %} +as-of | {{ short_sym }} | -{{ q.label or "" }} | +{{ q.label or "" }} | {{ q.price | price }} | -{{ q.currency or "" }} | +{{ q.currency or "" }} | {% for k in ["1d","1m","1y"] %} {% set v = q.changes.get(k) if q.changes else None %} -+ | {% if v is none %}—{% else %}{{ "%+.2f"|format(v) }}%{% endif %} | {% endfor %} {% if has_anchor %} {% set va = q.changes.get('anchor') if q.changes else None %} -+ | {% if va is none %}—{% else %}{{ "%+.2f"|format(va) }}%{% endif %} | {% endif %} -{{ q.as_of or "" }} | +{{ q.as_of or "" }} | {% endif %} {% endfor %}
|---|