Spaces:
Running
Running
| /* Mobile flow stylesheet. Pulls design tokens from shared/tokens.css | |
| so the palette matches the desktop. Visual treatments here mirror | |
| the desktop's hero copy, hero-card panel, capture-tips, primary | |
| button, image-frame, finger-cards, and size-ref-table — the mobile | |
| flow is the desktop page paginated, not a different visual | |
| identity. */ | |
| @import url("../shared/tokens.css"); | |
| * { | |
| box-sizing: border-box; | |
| } | |
| [hidden] { | |
| display: none ; | |
| } | |
| html, | |
| body { | |
| margin: 0; | |
| padding: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: var(--bg-1); | |
| color: var(--ink); | |
| font-family: "Iowan Old Style", "Palatino", "Book Antiqua", "Times New Roman", serif; | |
| -webkit-font-smoothing: antialiased; | |
| overflow: hidden; | |
| } | |
| #mobileRoot { | |
| width: 100%; | |
| /* Cascade order: each later rule overrides on browsers supporting | |
| that unit. Old browsers stop at vh; modern iOS Safari reaches | |
| dvh which tracks URL-bar transitions, so per-step footers stay | |
| visible. */ | |
| height: 100vh; | |
| height: 100svh; | |
| height: 100dvh; | |
| } | |
| /* --- Step base layout -------------------------------------------- */ | |
| .step { | |
| display: flex; | |
| flex-direction: column; | |
| width: 100%; | |
| height: 100%; | |
| padding: max(16px, env(safe-area-inset-top, 0px)) 20px | |
| max(16px, env(safe-area-inset-bottom, 0px)); | |
| background: | |
| radial-gradient(circle at 10% 10%, var(--bg-3), transparent 55%), | |
| radial-gradient(circle at 90% 0%, var(--bg-2), transparent 55%), | |
| linear-gradient(140deg, var(--bg-1), #fff8f2 60%, #f0e2d8 100%); | |
| } | |
| .step-head { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| margin-bottom: 12px; | |
| min-height: 40px; | |
| flex: 0 0 auto; | |
| } | |
| .step-back { | |
| width: 40px; | |
| height: 40px; | |
| flex: 0 0 40px; | |
| border: 1px solid var(--border); | |
| border-radius: 50%; | |
| background: white; | |
| color: var(--ink); | |
| font-size: 22px; | |
| cursor: pointer; | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .step-back:active { | |
| background: var(--sand); | |
| } | |
| .step-body { | |
| flex: 1 1 auto; | |
| overflow-y: auto; | |
| -webkit-overflow-scrolling: touch; | |
| } | |
| .step-foot { | |
| flex: 0 0 auto; | |
| padding-top: 14px; | |
| } | |
| /* Secondary action under a primary button — text-link weight so it | |
| reads as an "escape hatch" rather than an equally-weighted choice. | |
| Padding is sized to clear the WCAG 2.5.5 / Apple HIG ~44px minimum | |
| touch target. */ | |
| .step-link { | |
| display: block; | |
| width: 100%; | |
| margin-top: 8px; | |
| padding: 12px 16px; | |
| background: transparent; | |
| border: none; | |
| border-radius: 8px; | |
| color: var(--ink-soft); | |
| font-size: 0.95rem; | |
| font-weight: 500; | |
| text-decoration: underline; | |
| text-underline-offset: 3px; | |
| cursor: pointer; | |
| } | |
| .step-link:hover { | |
| color: var(--ink); | |
| } | |
| .step-link:focus-visible { | |
| color: var(--ink); | |
| outline: 2px solid var(--accent); | |
| outline-offset: 2px; | |
| } | |
| .guide-upload-error { | |
| margin: 8px 4px 0; | |
| font-size: 0.85rem; | |
| color: var(--accent); | |
| text-align: center; | |
| } | |
| /* --- Typography (matches desktop hero/panel) --------------------- */ | |
| .hero-eyebrow { | |
| text-transform: uppercase; | |
| letter-spacing: 0.18em; | |
| font-size: 0.75rem; | |
| font-weight: 600; | |
| color: var(--accent-dark); | |
| margin: 0 0 24px; | |
| } | |
| .hero-headline { | |
| font-family: "Futura", "Gill Sans", "Optima", "Trebuchet MS", sans-serif; | |
| font-size: clamp(2rem, 8vw, 2.4rem); | |
| letter-spacing: 0.02em; | |
| line-height: 1.15; | |
| margin: 0 0 28px; | |
| color: var(--ink); | |
| } | |
| .hero-sub { | |
| font-size: 1.05rem; | |
| line-height: 1.7; | |
| color: var(--ink-soft); | |
| margin: 0; | |
| max-width: 36ch; | |
| } | |
| /* Step 1 leans into a softly centered hero: vertically nudge the copy | |
| toward the middle so the eye lands on the headline, not the very | |
| top of the page. */ | |
| .step-intro .step-body { | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| } | |
| /* --- Panel (matches desktop .hero-card / .panel) ----------------- */ | |
| .panel { | |
| background: rgba(255, 255, 255, 0.78); | |
| border: 1px solid var(--border); | |
| border-radius: 20px; | |
| padding: 22px; | |
| box-shadow: 0 18px 40px rgba(43, 31, 31, 0.08); | |
| -webkit-backdrop-filter: blur(6px); | |
| backdrop-filter: blur(6px); | |
| margin-bottom: 16px; | |
| } | |
| .panel:last-child { | |
| margin-bottom: 0; | |
| } | |
| .panel-title { | |
| font-family: "Futura", "Gill Sans", "Optima", "Trebuchet MS", sans-serif; | |
| font-size: 1.4rem; | |
| margin: 0 0 14px; | |
| color: var(--ink); | |
| } | |
| /* --- Form controls (matches desktop) ----------------------------- */ | |
| .controls { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 14px; | |
| } | |
| .controls label { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 6px; | |
| font-size: 0.9rem; | |
| color: var(--ink-soft); | |
| } | |
| .controls input[type="text"], | |
| .controls input[type="email"], | |
| .controls select { | |
| /* Lock both controls to the same height. iOS Safari's native | |
| <select> renders at a slightly shorter intrinsic height than | |
| <input>, so identical padding alone doesn't match — an explicit | |
| height (with box-sizing: border-box, set globally) does. */ | |
| height: 48px; | |
| width: 100%; | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 0 14px; | |
| /* iOS auto-zoom guard — must be ≥ 16px. */ | |
| font-size: 1rem; | |
| line-height: 1.4; | |
| background: white; | |
| color: var(--ink); | |
| } | |
| /* Strip the native <select> chrome (Safari's tiny up/down chevrons, | |
| inset shadows, etc.) so the visual treatment matches the text | |
| input. We restore a single chevron via a background-image so the | |
| dropdown affordance is still visible. */ | |
| .controls select { | |
| -webkit-appearance: none; | |
| -moz-appearance: none; | |
| appearance: none; | |
| padding-right: 40px; | |
| background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='%234b3d3d' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'><polyline points='1,2 6,7 11,2'/></svg>"); | |
| background-repeat: no-repeat; | |
| background-position: right 14px center; | |
| background-size: 12px 8px; | |
| } | |
| .form-error { | |
| margin: 12px 0 0; | |
| font-size: 0.85rem; | |
| color: var(--accent); | |
| font-weight: 600; | |
| } | |
| /* --- Capture tips (matches desktop) ----------------------------- */ | |
| .capture-tips { | |
| margin: 0; | |
| padding: 14px 18px 14px 32px; | |
| list-style: disc; | |
| background: rgba(191, 58, 43, 0.06); | |
| border-left: 3px solid rgba(191, 58, 43, 0.55); | |
| border-radius: 8px; | |
| font-size: 0.9rem; | |
| color: var(--ink-soft); | |
| line-height: 1.55; | |
| } | |
| .capture-tips li + li { | |
| margin-top: 6px; | |
| } | |
| .capture-tips strong { | |
| color: var(--ink); | |
| } | |
| /* "Like this" sample below the tips on the guide step. Same rounded | |
| image-frame chrome as the confirm preview / result overlay so the | |
| visual treatment is consistent across the three places we show a | |
| photo. */ | |
| .guide-example { | |
| margin: 16px 0 0; | |
| padding: 0; | |
| } | |
| .guide-example img { | |
| width: 100%; | |
| height: auto; | |
| display: block; | |
| border-radius: 14px; | |
| background: #f6efea; | |
| box-shadow: 0 6px 18px var(--shadow); | |
| } | |
| .guide-example figcaption { | |
| margin-bottom: 8px; | |
| font-size: 0.85rem; | |
| color: var(--ink-soft); | |
| text-align: center; | |
| font-style: italic; | |
| } | |
| /* --- Primary button --------------------------------------------- */ | |
| .primary { | |
| width: 100%; | |
| border: none; | |
| border-radius: 14px; | |
| padding: 14px 16px; | |
| font-size: 1rem; | |
| font-weight: 600; | |
| color: white; | |
| background: linear-gradient(120deg, var(--accent), #e25f4f); | |
| box-shadow: 0 12px 30px var(--shadow); | |
| cursor: pointer; | |
| } | |
| .primary:active { | |
| transform: translateY(1px); | |
| } | |
| .primary:disabled { | |
| /* Distinct solid color rather than an opacity fade. Opacity makes | |
| a disabled button look like a "weak" version of the live one, | |
| which is visually noisy on top of a busy video preview. A solid | |
| warm beige-gray with no shadow reads unambiguously as inactive | |
| while staying inside the cream palette. */ | |
| background: #a89e8f; | |
| box-shadow: none; | |
| cursor: not-allowed; | |
| } | |
| /* --- Image frame (matches desktop input/result image panels) ---- */ | |
| .image-frame { | |
| position: relative; | |
| border-radius: 16px; | |
| overflow: hidden; | |
| background: #f6efea; | |
| min-height: 180px; | |
| display: grid; | |
| place-items: center; | |
| } | |
| .image-frame img { | |
| width: 100%; | |
| height: auto; | |
| display: none; | |
| } | |
| .image-frame.show img { | |
| display: block; | |
| } | |
| /* --- Confirm step status text ----------------------------------- */ | |
| /* Lives in the step-foot (not the panel) so it's always visible — | |
| tall photos can push panel content out of the visible step-body | |
| area, which would otherwise hide the live "Measuring… Xs" timer. */ | |
| .confirm-status { | |
| margin: 0 0 10px; | |
| font-size: 0.95rem; | |
| color: var(--ink-soft); | |
| text-align: center; | |
| /* Keep the timer line single-line so the layout doesn't jump as the | |
| seconds counter ticks; the message is short by design. */ | |
| white-space: nowrap; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| .confirm-status.error { | |
| color: var(--accent); | |
| font-weight: 600; | |
| /* Errors can be longer than the live timer; allow wrapping there. */ | |
| white-space: normal; | |
| } | |
| /* --- Step 4 — Capture stage (full-bleed camera) ----------------- */ | |
| .step-capture { | |
| --capture-url-bar-inset: 0px; | |
| --capture-bottom-inset: max( | |
| 28px, | |
| calc(var(--capture-url-bar-inset) + env(safe-area-inset-bottom, 0px) + 16px) | |
| ); | |
| position: relative; | |
| padding: 0; | |
| background: #000; | |
| } | |
| .capture-video { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| background: #000; | |
| } | |
| .capture-back, | |
| .capture-flash { | |
| position: absolute; | |
| top: max(12px, env(safe-area-inset-top, 12px)); | |
| width: 40px; | |
| height: 40px; | |
| padding: 0; | |
| border: none; | |
| border-radius: 50%; | |
| background: rgba(0, 0, 0, 0.45); | |
| color: #fff; | |
| font-size: 22px; | |
| cursor: pointer; | |
| z-index: 2; | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| -webkit-backdrop-filter: blur(8px); | |
| backdrop-filter: blur(8px); | |
| transition: background 0.15s ease, color 0.15s ease; | |
| } | |
| .capture-back { | |
| left: 12px; | |
| } | |
| .capture-flash { | |
| right: 12px; | |
| font-size: 18px; | |
| } | |
| .capture-back:hover, | |
| .capture-back:focus-visible, | |
| .capture-flash:hover, | |
| .capture-flash:focus-visible { | |
| background: rgba(0, 0, 0, 0.65); | |
| } | |
| .capture-flash[aria-pressed="true"] { | |
| background: #fff8d4; | |
| color: #8a5b0a; | |
| } | |
| .capture-chips { | |
| position: absolute; | |
| bottom: calc(var(--capture-bottom-inset) + 64px); | |
| left: 16px; | |
| right: 16px; | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 8px; | |
| justify-content: center; | |
| z-index: 2; | |
| } | |
| .capture-status { | |
| position: absolute; | |
| bottom: calc(var(--capture-bottom-inset) + 110px); | |
| left: 16px; | |
| right: 16px; | |
| margin: 0; | |
| text-align: center; | |
| font-size: 0.85rem; | |
| color: rgba(255, 255, 255, 0.85); | |
| text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6); | |
| z-index: 2; | |
| } | |
| .capture-status.error { | |
| color: #ffd5d0; | |
| font-weight: 600; | |
| } | |
| .capture-controls { | |
| position: absolute; | |
| bottom: var(--capture-bottom-inset); | |
| left: 16px; | |
| right: 16px; | |
| z-index: 2; | |
| } | |
| .capture-controls .capture-shutter { | |
| width: 100%; | |
| } | |
| /* --- Status chips (distance / level) ---------------------------- */ | |
| .status-chip { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 6px; | |
| padding: 4px 10px; | |
| border-radius: 999px; | |
| font-size: 0.82rem; | |
| font-weight: 600; | |
| border: 1px solid transparent; | |
| background: rgba(255, 255, 255, 0.85); | |
| color: var(--ink-soft); | |
| } | |
| .status-chip .chip-dot { | |
| display: inline-block; | |
| width: 8px; | |
| height: 8px; | |
| border-radius: 50%; | |
| background: currentColor; | |
| opacity: 0.8; | |
| } | |
| .status-chip.pending { | |
| color: var(--ink-soft); | |
| border-color: var(--border); | |
| } | |
| .status-chip.skipped { | |
| color: var(--ink-soft); | |
| border-color: var(--border); | |
| opacity: 0.6; | |
| } | |
| .status-chip.red { | |
| color: #b1271b; | |
| background: #fde7e3; | |
| border-color: rgba(177, 39, 27, 0.4); | |
| } | |
| .status-chip.amber { | |
| color: #8a5b0a; | |
| background: #fbeed1; | |
| border-color: rgba(138, 91, 10, 0.4); | |
| } | |
| .status-chip.green { | |
| color: #1f6b34; | |
| background: #d8f1de; | |
| border-color: rgba(31, 107, 52, 0.4); | |
| } | |
| /* --- Bubble level (capture stage) ------------------------------- */ | |
| /* iPhone-style two-cross level. A static white cross marks the center | |
| target; a second cross of the same shape drifts toward the high side | |
| of the device. When the device is in tolerance the moving cross snaps | |
| to the center and turns green (the two crosses visually merge). When | |
| out of tolerance it floats out, clamped to a max radius, in red. The | |
| ±5° tolerance ring is decorative — it gives the user a sense of how | |
| far "in tolerance" is — and stays neutral white. */ | |
| .capture-level-bubble { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| width: 128px; | |
| height: 128px; | |
| transform: translate(-50%, -50%); | |
| pointer-events: none; | |
| z-index: 2; | |
| --neutral-color: rgba(255, 255, 255, 0.85); | |
| --indicator-color: rgba(255, 255, 255, 0.85); | |
| filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.55)); | |
| } | |
| .capture-level-bubble[data-state="green"] { | |
| --indicator-color: #6ce39a; | |
| } | |
| .capture-level-bubble[data-state="red"] { | |
| --indicator-color: #ff8a7e; | |
| } | |
| /* Tolerance ring + center crosshair stay neutral so only the moving | |
| cross signals state (per design feedback). */ | |
| .capture-level-bubble .bubble-tolerance { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| width: 38px; | |
| height: 38px; | |
| border-radius: 50%; | |
| transform: translate(-50%, -50%); | |
| border: 1.5px solid var(--neutral-color); | |
| opacity: 0.85; | |
| } | |
| .capture-level-bubble .bubble-crosshair-h, | |
| .capture-level-bubble .bubble-crosshair-v { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| background: var(--neutral-color); | |
| opacity: 0.6; | |
| } | |
| .capture-level-bubble .bubble-crosshair-h { | |
| width: 14px; | |
| height: 1.5px; | |
| transform: translate(-50%, -50%); | |
| } | |
| .capture-level-bubble .bubble-crosshair-v { | |
| width: 1.5px; | |
| height: 14px; | |
| transform: translate(-50%, -50%); | |
| } | |
| /* Moving cross. The wrapper is the element JS translates every tick; | |
| the two arms inside form the cross relative to the wrapper's origin. */ | |
| .capture-level-bubble .bubble-indicator { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| width: 14px; | |
| height: 14px; | |
| transform: translate(-50%, -50%); | |
| /* Linear transform transition smooths the 100 ms detection tick; | |
| also gives the snap-to-center on green a brief settle motion. */ | |
| transition: transform 0.08s linear; | |
| } | |
| .capture-level-bubble .bubble-indicator-h, | |
| .capture-level-bubble .bubble-indicator-v { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| background: var(--indicator-color); | |
| transition: background 0.18s ease; | |
| } | |
| .capture-level-bubble .bubble-indicator-h { | |
| width: 18px; | |
| height: 2px; | |
| transform: translate(-50%, -50%); | |
| } | |
| .capture-level-bubble .bubble-indicator-v { | |
| width: 2px; | |
| height: 18px; | |
| transform: translate(-50%, -50%); | |
| } | |
| .capture-level-bubble[data-state="pending"] .bubble-indicator { | |
| opacity: 0; | |
| } | |
| /* --- Step 6 — Result -------------------------------------------- */ | |
| /* Top-of-result feedback banner. Mirrors the desktop's status text | |
| semantically (single line of feedback after measurement) but is | |
| visually a small panel-shaped card so it reads as a result rather | |
| than form chrome. Color-coded: green = success, red = failure. */ | |
| .result-status { | |
| margin: 0 0 16px; | |
| padding: 14px 16px; | |
| border-radius: 14px; | |
| background: white; | |
| border: 1px solid var(--border); | |
| border-left: 4px solid var(--ink-soft); | |
| box-shadow: 0 6px 18px var(--shadow); | |
| } | |
| .result-status p { | |
| margin: 0; | |
| font-size: 0.95rem; | |
| font-weight: 600; | |
| line-height: 1.5; | |
| } | |
| .result-status-success { | |
| background: #e8f4ec; | |
| border-color: rgba(31, 107, 52, 0.4); | |
| border-left-color: #1f6b34; | |
| } | |
| .result-status-success p { | |
| color: #1f6b34; | |
| } | |
| .result-status-error { | |
| background: #fde7e3; | |
| border-color: rgba(177, 39, 27, 0.4); | |
| border-left-color: var(--accent); | |
| } | |
| .result-status-error p { | |
| color: #b1271b; | |
| } | |
| /* Three finger cards stacked vertically (instead of the desktop's | |
| 3-column grid) — narrower mobile width fits a single column more | |
| comfortably without shrinking the size number. */ | |
| .finger-cards { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 10px; | |
| /* Padding-top (not margin-top) clears the floating "Recommended" | |
| badge that overhangs the first card at top: -12px. Margin-top | |
| would collapse with .panel-title's margin-bottom: 14px and produce | |
| no visible change; padding doesn't collapse. */ | |
| padding-top: 16px; | |
| margin-bottom: 12px; | |
| } | |
| .finger-card { | |
| position: relative; | |
| background: var(--sand); | |
| border-radius: 10px; | |
| padding: 14px 16px 16px; | |
| text-align: center; | |
| box-shadow: 0 1px 4px var(--shadow); | |
| } | |
| .finger-card .finger-badge { | |
| position: absolute; | |
| top: -12px; | |
| right: 12px; | |
| font-size: 0.78rem; | |
| font-weight: 700; | |
| text-transform: uppercase; | |
| letter-spacing: 0.06em; | |
| padding: 5px 12px; | |
| border-radius: 999px; | |
| background: var(--accent); | |
| color: #fff; | |
| line-height: 1; | |
| box-shadow: 0 1px 3px var(--shadow); | |
| } | |
| .finger-card .finger-name { | |
| font-weight: 600; | |
| font-size: 0.95rem; | |
| margin-bottom: 6px; | |
| text-transform: uppercase; | |
| letter-spacing: 0.04em; | |
| color: var(--ink); | |
| } | |
| .finger-card .finger-size-label { | |
| font-size: 0.75rem; | |
| text-transform: uppercase; | |
| letter-spacing: 0.06em; | |
| color: var(--ink-soft); | |
| margin-top: 4px; | |
| } | |
| .finger-card .finger-size { | |
| font-size: 2rem; | |
| font-weight: 700; | |
| color: var(--accent); | |
| line-height: 1.1; | |
| margin: 2px 0; | |
| } | |
| .finger-card .finger-range { | |
| font-size: 0.9rem; | |
| color: var(--ink-soft); | |
| } | |
| .finger-card .finger-width { | |
| font-size: 0.9rem; | |
| color: var(--ink-soft); | |
| margin-top: 4px; | |
| } | |
| .finger-card-failed { | |
| opacity: 0.85; | |
| } | |
| .finger-card .finger-failed { | |
| font-size: 1rem; | |
| font-weight: 600; | |
| color: #721c24; | |
| margin: 8px 0 4px; | |
| } | |
| .finger-card .finger-fail-reason { | |
| font-size: 0.78rem; | |
| color: var(--ink-soft); | |
| word-break: break-word; | |
| } | |
| .finger-count { | |
| text-align: center; | |
| font-size: 0.85rem; | |
| color: var(--ink-soft); | |
| margin-top: 8px; | |
| margin-bottom: 12px; | |
| } | |
| /* --- Size reference table (mirrors desktop) --------------------- */ | |
| .size-ref-table { | |
| margin-top: 14px; | |
| padding-top: 12px; | |
| border-top: 1px solid var(--border); | |
| } | |
| .size-ref-title { | |
| font-family: "Futura", "Gill Sans", "Optima", "Trebuchet MS", sans-serif; | |
| font-size: 1rem; | |
| margin: 0 0 8px; | |
| color: var(--ink); | |
| /* Center the model-label heading so it lines up with the centered | |
| SIZE / Inner Diameter (mm) columns below — same alignment story as | |
| the desktop. */ | |
| /*text-align: center;*/ | |
| } | |
| .size-ref-table table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| font-size: 0.9rem; | |
| } | |
| .size-ref-table th, | |
| .size-ref-table td { | |
| padding: 8px 10px; | |
| /* Match the desktop alignment — both headers and values are | |
| centered, which is more comfortable to scan when the columns | |
| are short numeric values. */ | |
| text-align: center; | |
| border-bottom: 1px solid var(--border); | |
| } | |
| .size-ref-table th { | |
| font-weight: 600; | |
| color: var(--ink-soft); | |
| font-size: 0.8rem; | |
| text-transform: uppercase; | |
| letter-spacing: 0.06em; | |
| } | |
| .size-ref-table tbody tr:nth-child(even) { | |
| background: rgba(245, 241, 231, 0.5); | |
| } | |
| /* --- Feedback panel (mirrors desktop .feedback-*) ---------------- */ | |
| .feedback-panel .feedback-hint { | |
| margin: 0 0 12px; | |
| color: var(--ink-soft); | |
| font-size: 0.95rem; | |
| } | |
| .feedback-form { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 10px; | |
| } | |
| .feedback-rating { | |
| display: flex; | |
| gap: 6px; | |
| } | |
| .star-btn { | |
| background: none; | |
| border: none; | |
| padding: 4px 6px; | |
| font-size: 2rem; | |
| line-height: 1; | |
| color: rgba(45, 33, 33, 0.25); | |
| cursor: pointer; | |
| transition: color 0.15s ease, transform 0.1s ease; | |
| } | |
| .star-btn.star-filled { color: #e3a73b; } | |
| .feedback-form textarea { | |
| width: 100%; | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 10px 12px; | |
| /* 1rem keeps iOS Safari from auto-zooming the page on focus | |
| (same reason the .controls inputs use 1rem). */ | |
| font-size: 1rem; | |
| font-family: inherit; | |
| background: white; | |
| color: var(--ink); | |
| resize: vertical; | |
| min-height: 80px; | |
| } | |
| .feedback-row { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| flex-wrap: wrap; | |
| flex-direction: row-reverse; | |
| justify-content: flex-start; | |
| } | |
| .feedback-submit { | |
| width: auto; | |
| padding: 10px 22px; | |
| } | |
| .feedback-status { | |
| font-size: 0.9rem; | |
| color: var(--ink-soft); | |
| } | |
| .feedback-status-ok { color: #2f7a3d; } | |
| .feedback-status-error { color: var(--accent); font-weight: 600; } | |