/* Responsive & Accessibility * * The ONLY file in the app allowed to contain @media (enforced by * styles.test.ts) — every viewport/motion/contrast override lives here so * there is one place to check for breakpoints instead of one per feature * file. */ /* Mobile - Small screens (max 640px) */ @media (max-width: 640px) { /* Header — the same four groups (brand / nav / identity / tools) reorder * into three rows: brand + language + logout, the nav as a full-width * row, then the child select full-width below. 16px gutters. */ .header__bar { padding: 0 var(--sp-4); row-gap: var(--sp-2); padding-bottom: var(--sp-2); } .header__brand { order: 1; flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-base); } .header__tools { order: 2; flex-shrink: 0; } .header__nav { order: 3; flex: 0 0 100%; display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-1); } .header__nav-link { width: 100%; justify-content: center; } .header__identity { order: 4; flex: 0 0 100%; margin-left: 0; } .header__child-select { width: 100%; max-width: none; padding-left: 0; } .header__parent-name { display: none; } /* Main content — the container picks up the horizontal gutter; sections * themselves stay at zero horizontal padding at every width. */ .layout__main { padding: var(--sp-5) var(--sp-4); } .dashboard__section, .settings-page__section, .calendar__section, .tips-page__section { padding-top: var(--sp-4); margin-bottom: var(--sp-4); } /* Forms - full width */ .form-group input, .form-group select, .form-group textarea, .vacation-days-input__field, .planning-goal-selector__select, .parent-profile-input__field { max-width: none; } /* Dashboard — the three key figures tighten; a year row keeps year + * track on the first line and wraps the per-parent figures under the * track, each parent on its own line. */ .key-figures { gap: var(--sp-4) var(--sp-6); } .year-row__summary { grid-template-columns: 3rem minmax(0, 1fr) var(--icon-sm); } .year-row__summary::after { grid-column: 3; } .year-row__figures { grid-row: 2; grid-column: 2 / -1; display: flex; flex-direction: column; gap: var(--sp-1); white-space: normal; } .year-row__parent { display: flex; flex-wrap: wrap; column-gap: var(--sp-3); row-gap: 0; } .year-row__body { padding-left: 0; } .year-row__table, .parental-leave-breakdown__table { width: 100%; } /* Modals — the overlay already gutters the content with its own padding * and `.modal__content`'s `width: min(100%, var(--modal-w))` already caps * it to the viewport, so only the footer's button stacking needs a * mobile-specific rule. */ .modal__footer { flex-direction: column-reverse; } .modal__footer .btn { width: 100%; } .qrcode-modal__qr-image { max-width: 250px; } /* Delete-confirmation row: the warning text gets its own line, and the * two buttons split the row evenly. */ .modal__delete-confirm-text { width: 100%; margin-right: 0; } .modal__delete-confirm .btn { flex: 1; min-width: 0; } /* The footer stacks bottom-up here (column-reverse above), so the * revealed delete scopes sort FIRST to land at the bottom, directly under * the "Ta bort…" button that opened them. */ .period-popover__delete-group { order: -10; } /* Loading spinner */ .loading-spinner { padding: var(--sp-5); min-height: 200px; } /* Calendar — cells are ~48px wide at 390px, so the padding tightens and * the numeral keeps its 24px box flush to the top-left. */ .calendar__weekday { padding: 0 var(--sp-1) var(--sp-2); } .calendar-day { min-height: 64px; padding: var(--sp-1) var(--sp-1) calc(var(--sp-1) + var(--band-reserved-height, 0px)); } .calendar__week-number { padding-top: var(--sp-1); } .calendar-band__label { padding: 0 var(--sp-1); } /* The preset chips scroll sideways in one row instead of wrapping, so the * grid is never pushed down the page. */ .preset-chips__row { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; padding-bottom: var(--sp-1); } .preset-chips__row::-webkit-scrollbar { display: none; } /* Footer — same side gutter as .layout__main at this width. */ .footer__inner { padding: 0 var(--sp-4); } } /* Tablet - Medium screens (641px to 1024px) */ @media (min-width: 641px) and (max-width: 1024px) { .layout__main { padding: var(--sp-6) var(--sp-5); } } /* Accessibility - Reduced Motion * * The universal `*` rule collapses every transition/animation duration to * near-zero already; there is deliberately no second, narrower list of * selectors repeating that on top of it. */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; } } /* High Contrast Mode — the darkest step of the accent, pure black text and * a visibly darker hairline. */ @media (prefers-contrast: more) { :root { --c-text: #000; --c-text-muted: #333; --c-accent: #1d3ca3; --c-border: #555; } button:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible { outline-width: 3px; } } /* Keyboard Navigation */ :focus-visible { outline: 2px solid var(--c-accent); outline-offset: 2px; }