/* ==========================================================================
   Portrait phone optimisations.
   Loaded after style.css; everything here is inside a max-width query so the
   desktop layout is untouched.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Page transitions
   -------------------------------------------------------------------------- */
@keyframes pageIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes pageOut { to { opacity: 0; transform: translateY(-6px); } }

/* backwards, not both.

   `both` keeps the final keyframe applied for ever after the animation ends,
   and a *filled* `transform: none` does not compute to none -- it computes to
   the identity matrix. Any computed transform other than none makes the
   element the containing block for its position:fixed descendants, so .content
   was permanently capturing every modal backdrop inside it: `inset: 0` then
   meant the whole document rather than the viewport, and a dialog centred
   itself around the middle of the page instead of the screen. On a long page
   like the admin panel that put every dialog well below the fold.

   `backwards` still covers the state before the animation starts, which is all
   the fill was ever needed for. Once it finishes the element goes back to its
   own style, where the transform really is none. Nothing moves at the join:
   the last keyframe and the base style are the same picture. */
.content, .landing, .auth-wrap { animation: pageIn 320ms var(--ease-out) backwards; }
/* Applied just before navigating so the outgoing page doesn't just vanish. */
body.is-leaving .content,
body.is-leaving .landing { animation: pageOut 170ms var(--ease) both; }

/* Chrome's native cross-document transitions, where available.

   This carried a staircase for a while -- the arriving page uncovered in five
   staggered columns -- and it is back to the plain cross-fade it was before
   that. Firefox and older Safari never had the transition either way and
   still get the pageIn/pageOut above. */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: pageOut 170ms var(--ease) both; }
::view-transition-new(root) { animation: pageIn 300ms var(--ease-out) both; }

/* The document runs pageIn on arrival as well, so without this the fade plays
   twice for one navigation -- once on the browser's snapshot and once on the
   live page. That was true before the staircase and would be true again
   without it, so the suppression stays.

   `animation: none` and deliberately not a transform: a transform here makes
   .content the containing block for every dialog inside it, which is what put
   all five admin dialogs below the fold. */
:root.vt-running .content,
:root.vt-running .landing,
:root.vt-running .auth-wrap { animation: none; }

/* Horizontal rails: swipeable rows instead of tall stacks. */
.h-scroll {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  /* Bleed to the screen edges so the row reads as scrollable. */
  margin-inline: -15px;
  padding-inline: 15px;
  padding-bottom: 4px;
}
.h-scroll::-webkit-scrollbar { display: none; }
.h-scroll > * { scroll-snap-align: start; flex: 0 0 auto; }

@media (max-width: 768px) {
  /* ------------------------------------------------------------------
     Touch targets — 44px minimum, per platform guidance
     ------------------------------------------------------------------ */
  .btn, .btn-ask, .icon-btn, .tab-btn, .nav-link, .mini-nav button {
    min-height: 44px;
  }
  /* Height alone left the round topbar icons at 42x44. They are circles, so
     the narrow axis is the one a thumb actually has to hit. */
  .icon-btn { min-width: 44px; }

  /* The same omission, found again in three more places. Every one of these
     was 44px tall and far narrower, which is the shape a height-only rule
     produces: it looks handled in the CSS and misses under a thumb.
     Measured before the fix -- week arrows 24.8px, month arrows 29px, the
     timetable overflow button 42px.

     These are the worst possible controls to have small, because they are
     the only way to move between weeks and months on a phone, and a miss
     lands on nothing at all rather than on the wrong thing. */
  .tt-step, .tt-overflow .btn { min-width: 44px; }
  .mini-nav button { min-width: 44px; }
  /* 44px is the platform minimum for a reliable tap. */
  .btn-sm { min-height: 44px; padding: 10px 14px; font-size: 0.82rem; }
  .task-check { width: 26px; height: 26px; }
  .link-more { min-height: 40px; display: inline-flex; align-items: center; }

  /* ------------------------------------------------------------------
     Shell: bottom tab bar replaces the drawer for primary navigation
     ------------------------------------------------------------------ */
  /* Clear the tab bar: what it stands off the bottom, plus its height, plus
     room to breathe. Built from the same two tokens the bar itself is placed
     with, so the reservation cannot drift away from the thing being reserved
     for. */
  .content {
    padding-bottom: calc(20px + var(--bottom-nav-h) + var(--bottom-nav-gap));
  }

  /* One source of truth: anything that has to sit clear of the tab bar reads
     these rather than guessing.

     The gap is how far the floating bar sits off the bottom of the screen. It
     reaches 16px into the home-indicator inset rather than clearing all of it
     -- sitting the full 34px up left a visible band of dead bar under the
     icons -- but never comes closer than 8px to the edge, which is what keeps
     it off a phone that has no inset to spend. */
  :root {
    --bottom-nav-h: 60px;
    --bottom-nav-gap: max(8px, calc(var(--safe-bottom) - 16px));
    --topbar-h: 65px;
  }

  /* A rounded bar floating clear of the edges, rather than one bolted across
     the bottom of the screen.

     Bolted down, it had to be padded away from the home indicator from the
     inside, which left a band of empty bar below the icons and a hard square
     corner against a screen that has none. Floating, the inset becomes the
     gap underneath it and the corners can follow the phone's own.

     The radius is roughly the screen's own less the distance the bar is inset
     from it, which is what makes the two curves read as concentric rather
     than as one corner inside another. */
  .bottom-nav {
    position: fixed; z-index: 70;
    left: calc(10px + var(--safe-left));
    right: calc(10px + var(--safe-right));
    bottom: var(--bottom-nav-gap);
    display: flex; justify-content: space-around; align-items: stretch;
    background: rgba(255, 255, 255, 0.94);
    backdrop-filter: blur(14px);
    border: 1px solid var(--border);
    border-radius: 26px;
    /* So the travelling pill and the active-tab marker cannot square off the
       corners the bar just gained. */
    overflow: hidden;
    box-shadow: 0 10px 30px rgba(76, 52, 42, 0.16), 0 2px 8px rgba(76, 52, 42, 0.07);
  }
  /* Nothing shows underneath the bar.

     It floats ten pixels off three edges, so the page scrolled past it in the
     gap below and in the strips either side -- a line of text sliding through
     the space under the tab bar, which reads as a rendering fault rather than
     as depth. .content already reserves the room; this covers what passes
     through the reservation while it is being scrolled.

     Painted on the body rather than inside the bar: .bottom-nav carries a
     backdrop-filter, which makes it the containing block for its own
     fixed-position children, so a strip drawn in there could never reach the
     edges of the screen. Scoped with :has so it does not appear on the pages
     that have no tab bar to hide anything for. */
  body:has(.bottom-nav)::after {
    content: "";
    position: fixed;
    left: 0; right: 0; bottom: 0;
    height: calc(var(--bottom-nav-gap) + var(--bottom-nav-h));
    background: var(--bg);
    /* Under the bar, over the page. */
    z-index: 69;
    pointer-events: none;
  }

  .bn-item {
    flex: 1;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 3px; padding: 9px 4px 7px;
    /* 0.63rem was 10.08px -- the smallest text in the app, on its primary
       navigation. Five labels still fit across a 320px screen at this size;
       that was measured, not assumed. */
    font-size: 0.7rem; font-weight: 700;
    color: var(--text-muted);
    position: relative;
    transition: color var(--t-fast) var(--ease);
    -webkit-tap-highlight-color: transparent;
  }
  .bn-ico { font-size: 1.18rem; line-height: 1; transition: transform var(--t) var(--ease-spring); }
  .bn-item:active .bn-ico { transform: scale(0.86); }
  .bn-item.active { color: var(--primary); }
  .bn-item.active .bn-ico { transform: translateY(-2px) scale(1.08); }
  /* Indicator bar above the active tab. Kept as the fallback: if nav-pill.js
     has not run there is still something marking where you are. */
  .bn-item.active::before {
    content: ""; position: absolute; top: 0; left: 50%;
    width: 26px; height: 3px; margin-left: -13px;
    background: var(--primary); border-radius: 0 0 4px 4px;
    animation: scaleIn var(--t) var(--ease-spring) both;
  }
  .bottom-nav.has-pill .bn-item.active::before { display: none; }

  /* The travelling pill, fluid-tabs style: a soft raised chip on the bar
     rather than the sidebar's solid coral. The label here is already coral,
     and a coral pill under coral text would take the label with it.

     Fully rounded, as in the source, and inset by nav-pill.js so it reads as
     a pill sitting on a track instead of a block filling the bar. */
  .bottom-nav .nav-pill {
    border-radius: 999px;
    background: var(--primary-softer);
    border: 1px solid var(--primary-soft);
    box-shadow: var(--shadow-xs);
  }
  .bn-badge {
    position: absolute; top: 5px; right: 50%; margin-right: -20px;
    background: var(--primary); color: #fff;
    font-size: 0.58rem; font-weight: 800;
    min-width: 16px; height: 16px; padding: 0 4px;
    border-radius: 99px; display: grid; place-items: center;
  }

  /* The drawer now only holds secondary items, reached via "More". */
  .sidebar { padding-bottom: calc(90px + var(--safe-bottom)); }

  /* ------------------------------------------------------------------
     Topbar: tighter, search collapses to an icon
     ------------------------------------------------------------------ */
  /* Turning on viewport-fit=cover moved the top of the page under the status
     bar and the notch as well as freeing the bottom, so the bar that sits
     there has to come down by the same amount. In a browser tab the inset is
     zero and this is exactly what it was. */
  .topbar {
    /* Shaped like the tab bar at the other end of the screen, and no longer
       pinned to the top of it.

       The two changes belong together. A bar that is inset from the edges
       with corners following the phone's own reads as furniture floating on
       the page; the same bar held in place while the page slides underneath
       reads as a second tab bar, and costs 65px of a short screen on every
       page to say so.

       The trade is real and worth stating: the bell, the theme toggle and the
       account menu are now reachable at the top of the page rather than from
       anywhere in it. */
    position: sticky;
    top: calc(8px + var(--safe-top));
    z-index: 55;
    margin: calc(8px + var(--safe-top)) calc(10px + var(--safe-right)) 8px
            calc(10px + var(--safe-left));
    /* `translate`, not `transform`: nothing here uses transform today, but a
       transform would be the one declaration that silently overwrites a hover
       or :active state if one is ever added to this bar. */
    transition: translate 260ms var(--ease-out);
    padding: 8px 14px;
    gap: 8px;
    /* Roughly the screen's own radius less the distance the bar is inset from
       it, which is what makes the two curves read as concentric. The same
       figure the tab bar uses, because they are the same object twice. */
    border-radius: 26px;
  }
  /* Out of the way going down, back the moment you head up.

     Sticky rather than in flow: it keeps its place in the document, so
     nothing below it has to reserve room, and it is free to stay put once
     the page has scrolled past. What stops that being the frozen bar it
     replaced is that reading downwards tucks it away entirely -- the page
     gets the whole screen -- while any upward movement brings it back, so
     the bell and the account menu are one gesture away rather than a scroll
     to the top of the page.

     Far enough up to clear its own top margin as well, or a sliver of the
     pane is left hanging under the notch. */
  .topbar.is-tucked {
    translate: 0 calc(-100% - 16px - var(--safe-top));
  }

  .content {
    padding-left: calc(15px + var(--safe-left));
    padding-right: calc(15px + var(--safe-right));
  }
  /* 32px wide against a 44px minimum: this is the control that opens the
     account menu and the Personal/Work switch, so it is the last one that
     should need a second tap. */
  .user-chip { padding: 3px; min-width: 44px; justify-content: center; }
  .user-avatar { width: 32px; height: 32px; }

  /* ------------------------------------------------------------------
     Dashboard: put the schedule first, rails for the rest
     ------------------------------------------------------------------ */
  .content-main { display: flex; flex-direction: column; }
  .hero { order: 1; padding: 20px 18px; }
  .grid-4#stat-grid { order: 2; }
  .quick-actions { order: 4; }
  section.grid-2 { order: 3; }   /* Today's Schedule + Upcoming */
  .content-rail { order: 5; }

  .hero-greet { font-size: 1.22rem; }
  .hero-sub { font-size: 0.85rem; }
  .hero::before, .hero::after { display: none; }  /* decorative arcs cost paint */

  .ai-input-row { padding: 5px 5px 5px 14px; margin-top: 14px; }
  .ai-input-row input { font-size: 16px; }  /* < 16px makes iOS zoom on focus */
  .btn-ask { padding: 11px 16px; font-size: 0.86rem; }

  /* Chips: one swipeable row rather than a 167px stack */
  .ai-examples {
    flex-wrap: nowrap; overflow-x: auto;
    scroll-snap-type: x proximity; scrollbar-width: none;
    margin-inline: -18px; padding-inline: 18px; padding-bottom: 2px;
  }
  .ai-examples::-webkit-scrollbar { display: none; }
  .ai-example-chip { flex: 0 0 auto; scroll-snap-align: start; }

  /* Stat tiles: 2x2, not a swipeable rail.
     The rail fitted 2.6 of four 148px cards into a 393px window, so the third
     was sliced down the middle at the screen edge and the fourth was off it
     entirely with nothing saying so -- which reads as broken layout rather
     than as something to swipe. The grid costs about 90px of height and shows
     all four, cut nothing, and needs no gesture to discover. */
  #stat-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
    overflow-x: visible;
    margin-inline: 0; padding-inline: 0;
  }
  #stat-grid .stat-card {
    min-width: 0; padding: 13px;
  }
  #stat-grid .stat-value { font-size: 1.45rem; }
  #stat-grid .stat-ico { width: 36px; height: 36px; font-size: 1rem; border-radius: 11px; }
  #stat-grid .stat-sub { display: none; }
  #stat-grid .sparkline { display: none; }

  /* Quick actions: two per row reads better than one tall column */
  .quick-actions { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .qa-card { padding: 13px 14px; font-size: 0.8rem; gap: 9px; }
  .qa-card .qa-ico { width: 28px; height: 28px; font-size: 0.85rem; }

  /* ------------------------------------------------------------------
     Timetable: a 7-column grid is unusable at 375px, so show one day
     ------------------------------------------------------------------ */
  /* The bar itself is styled globally now, in style.css. Only the phone's
     own proportions belong here: the week nav takes the width the "+ Add"
     and overflow buttons leave, so the bar fills the row instead of huddling
     at one end of it. */
  .tt-week-nav { flex: 1 1 auto; }
  .tt-week-nav .tt-today { flex: 1 1 auto; }

  /* Day picker */
  .tt-daybar {
    display: flex; gap: 6px; margin: 12px 0;
    overflow-x: auto; scrollbar-width: none;
    margin-inline: -15px; padding-inline: 15px;
  }
  .tt-daybar::-webkit-scrollbar { display: none; }
  .tt-day-btn {
    flex: 0 0 auto; min-width: 52px;
    display: flex; flex-direction: column; align-items: center; gap: 2px;
    padding: 9px 8px; border-radius: 13px;
    background: var(--surface); border: 1px solid var(--border);
    font-weight: 700; font-size: 0.7rem; color: var(--text-soft);
    transition: background var(--t) var(--ease), color var(--t) var(--ease), transform var(--t) var(--ease);
  }
  .tt-day-btn .tt-day-num { font-size: 1rem; font-weight: 800; }
  .tt-day-btn.active {
    background: linear-gradient(135deg, var(--primary) 0%, #eb9375 100%);
    color: #fff; border-color: transparent;
    box-shadow: 0 5px 14px var(--primary-ring);
  }
  .tt-day-btn.has-events:not(.active)::after {
    content: ""; width: 4px; height: 4px; border-radius: 50%;
    background: var(--primary); margin-top: 1px;
  }
  .tt-day-btn:active { transform: scale(0.95); }

  /* Single-day agenda replaces the grid */
  .timetable-grid { display: none; }
  .tt-dayview { display: block; animation: pageIn 260ms var(--ease-out) both; }
  .tt-dayview .tl-item { grid-template-columns: 58px 16px 1fr; }
  .tt-dayview .tag { grid-column: 3; justify-self: start; margin-top: 6px; }

  /* ------------------------------------------------------------------
     Calendar, tables, modals
     ------------------------------------------------------------------ */
  .calendar-toolbar { gap: 7px; }
  .calendar-view-switch { width: 100%; margin-left: 0; order: 3; }
  .calendar-view-switch .btn { flex: 1; }
  .month-cell { min-height: 62px; padding: 5px; font-size: 0.72rem; }
  .month-grid { gap: 4px; }

  .data-table { font-size: 0.78rem; }
  .data-table th, .data-table td { padding: 9px 8px; }

  /* Dialogs open in the middle.

     They used to be bottom sheets, sliding up from the bottom edge on the
     theory that a sheet is easier to reach one-handed. That is true of a
     notification list you skim and flick away, and wrong for these: a task
     sheet or a community sheet is a thing you read and edit, and arriving
     from off-screen makes it read as sliding in from behind the page rather
     than opening on top of it.

     Centred and scaled in place instead, the same as on a desktop, so a
     dialog is the same object on every screen. */
  .modal-backdrop {
    align-items: center;
    justify-items: center;
    padding: 16px;
  }
  .modal-card {
    max-width: none;
    width: 100%;
    /* Rounded on all four corners now that it is not sitting on an edge. */
    border-radius: var(--radius-lg);
    /* dvh, not vh: with a browser toolbar on screen vh overshoots and the
       bottom of the dialog ends up under it. */
    max-height: min(86dvh, calc(100dvh - 32px));
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 20px 18px;
    animation: scaleIn 220ms var(--ease-out) both;
  }

  /* The tab bar is still taken out while a dialog is open. It is not a usable
     target mid-edit, and leaving it lit under a scrim invites a tap that
     cannot land. */
  body:has(.modal-backdrop:not(.hidden)) .bottom-nav,
  body:has(.modal-backdrop:not(.hidden))::after { display: none; }

  .modal-actions .btn { flex: 1; min-width: 44%; }

  /* Clear of the tab bar, which is 64px plus whatever the home indicator
     adds -- so a fixed 82px sat a toast behind the bar on any phone with one.
     The sides move in for the curved corners in landscape. */
  .toast-container {
    bottom: calc(14px + var(--bottom-nav-h) + var(--bottom-nav-gap));
    left: calc(15px + var(--safe-left));
    right: calc(15px + var(--safe-right));
  }
}

@keyframes sheetDown { from { transform: translateY(-100%); } to { transform: none; } }

/* Very narrow phones */
@media (max-width: 340px) {
  .quick-actions { grid-template-columns: minmax(0, 1fr); }
  .hero-greet { font-size: 1.12rem; }
  /* The tab labels used to shrink again here, to 9.28px, which is backwards:
     the smallest screen is where legibility is already hardest, and shrinking
     type is not how five items are made to fit. They fit at full size --
     measured at 320px -- so there is nothing left to trade away. */
}

/* Landscape phones: the tab bar would eat too much of a short viewport. */
@media (max-width: 900px) and (orientation: landscape) and (max-height: 480px) {
  .bottom-nav, body:has(.bottom-nav)::after { display: none; }
  .content { padding-bottom: 20px; }
}

@media (prefers-reduced-motion: reduce) {
  /* It still gets out of the way; it just does not travel to do it. */
  .topbar { transition: none; }
  .content, .landing, .auth-wrap, .modal-card, .tt-dayview { animation: none !important; }
}

/* ==========================================================================
   Short viewports (small laptops, split screens)
   The sidebar is a flex column; without this the AI assistant card and quote
   sit below the fold or get squeezed, so they read as missing.
   ========================================================================== */
@media (min-width: 769px) and (max-height: 760px) {
  .sidebar { padding-top: 16px; gap: 3px; }
  .brand-block { padding-bottom: 14px; }
  .nav-link { padding: 9px 14px; font-size: 0.875rem; }
  /* The quote is pure decoration — first thing to go when space is tight. */
  .sidebar-quote { display: none; }
  .assistant-card { padding: 12px; margin-top: auto; }
  .assistant-sub { margin: 4px 0 9px; font-size: 0.72rem; }
}

@media (min-width: 769px) and (max-height: 620px) {
  /* Collapse the assistant card to just its button. */
  .assistant-card { background: none; border: none; padding: 0; }
  .assistant-title, .assistant-sub { display: none; }
  .assistant-btn { padding: 10px 12px; }
}

/* --------------------------------------------------------------------------
   Profile: the two fields that became dropdowns
   -------------------------------------------------------------------------- */
@media (max-width: 768px) {
  /* Measured at 360px: the department select came out 41px. A select opens a
     menu, so it is a tap target in a way the text fields beside it are not.
     (The rest of this form's inputs sit at 39px -- pre-existing, left alone.) */
  #pf-department-select { min-height: 44px; }
}


/* ==========================================================================
   Phone layout, measured at 393px
   ========================================================================== */
@media (max-width: 768px) {
  /* "Free Hours" was breaking to "Fr / He" inside a squeezed card. */
  .stat-label, .stat-card .stat-sub { overflow-wrap: normal; hyphens: none; }

  /* --- The AI row ------------------------------------------------------
     Input, mic and Ask AI on one line left the input 137px wide, so the
     placeholder read "Ask me to man". The question is the point of the row;
     it gets the full width and the two controls sit under it. */
  .ai-input-row {
    flex-wrap: wrap;
    row-gap: 9px;
    padding: 10px;
    border-radius: var(--radius);
  }
  .ai-input-row input {
    flex: 1 1 100%;
    min-width: 0;
    /* Its own row now, so it can be a real target rather than a 27px slot
       between two buttons. */
    padding: 13px 8px;
  }
  .ai-input-row .ai-mic { flex: 0 0 auto; }
  .ai-input-row .btn-ask { flex: 1 1 auto; justify-content: center; }

  /* --- Horizontal scrollers -------------------------------------------
     The example chips genuinely scroll (1285px of them). A hard cut at the
     screen edge reads as broken layout, so the last one fades instead --
     which is the only thing on screen saying "keep going". */
  .ai-examples {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 34px), transparent 100%);
    mask-image: linear-gradient(to right, #000 calc(100% - 34px), transparent 100%);
    scroll-snap-type: x proximity;
    scroll-padding-left: 15px;
  }
  .ai-example-chip { scroll-snap-align: start; }

  /* --- Card headers ----------------------------------------------------
     "Today's Schedule" and "View Timetable" were each wrapping to two lines
     while sharing one row. The title takes the row; the link sits under it,
     still a 40px target. */
  .card-head { flex-wrap: wrap; row-gap: 4px; align-items: baseline; }
  .card-title { flex: 1 1 100%; min-width: 0; }
  .card-head .link-more { margin-left: auto; }
}

@media (max-width: 380px) {
  #stat-grid { gap: 8px; }
  #stat-grid .stat-card { padding: 11px; }
}

/* ==========================================================================
   Touch targets, from a DOM sweep at 393px
   Every interactive element on every page was measured; these are the ones
   that came back under 44px. The worst was a filter select at 18px.
   ========================================================================== */
@media (max-width: 768px) {
  input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):not([type="range"]),
  select,
  textarea {
    min-height: 44px;
  }
  /* Its own rule: this one measured 18px, collapsed by a flex parent that
     gave it no height of its own. */
  select { height: auto; }

  .assistant-btn,
  .link-more,
  .ai-example-chip {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
  /* The chips row is a scroller, so taller chips must not also grow its
     scroll height. */
  .ai-examples { align-items: stretch; }
}

@media (max-width: 768px) {
  /* A 13px checkbox is a 13px target, and its label was only 20px tall -- so
     neither half of the control was tappable. Padding rather than min-height:
     these labels already carry their own flex layouts (the assign picker puts
     two lines beside the box), and forcing a height would fight them. */
  label:has(> input[type="checkbox"]),
  label:has(> input[type="radio"]) {
    padding-block: 12px;
  }
  input[type="checkbox"],
  input[type="radio"] {
    width: 20px; height: 20px;
    flex: 0 0 auto;
  }
}

/* ==========================================================================
   Running inside the native shell
   Only applies when native.js has set .is-native, so the browser build is
   untouched by any of it.
   ========================================================================== */
.is-native .topbar {
  /* iOS draws the status bar over the top of the web view. On a phone the
     bar's own top margin already carries that inset, so adding it to the
     padding as well would clear the notch twice. Wider layouts keep the
     original behaviour, where the bar is still bolted to the top. */
  padding-top: calc(10px + var(--safe-top));
}
@media (max-width: 768px) {
  .is-native .topbar { padding-top: 8px; }
}
/* The bar floats and the content reserves for it from the shared tokens, both
   of which already read the same insets the native shell reports -- so there
   is nothing left for this to override. */
/* The notification sheet hangs off the top bar, which just got taller. */
.is-native .popover {
  top: calc(var(--topbar-h, 65px) + var(--safe-top));
}

/* A field focused behind the keyboard cannot be read while it is typed in. */
.keyboard-open .bottom-nav,
.keyboard-open body:has(.bottom-nav)::after { display: none; }
.keyboard-open .content { padding-bottom: calc(var(--keyboard-h, 0px) + 16px); }

/* Offline, said once, out of the way of the thing being read. */
.native-offline {
  position: fixed; left: 0; right: 0;
  top: calc(var(--safe-top));
  z-index: 300;
  padding: 7px 14px;
  background: var(--warning, #d9a05b);
  color: #1b1410;
  font-size: 0.78rem; font-weight: 700; text-align: center;
  animation: veilIn 200ms var(--ease-out) both;
}

/* A native app should not feel like a page that can be dragged off its
   moorings, and text selection on a tap-and-hold reads as a bug. */
.is-native body { overscroll-behavior-y: none; }
.is-native .nav-link, .is-native .bn-item, .is-native .icon-btn, .is-native .btn {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

/* The landing page has its own bar, so the safe-area padding applied to
   .topbar never reached it. On iOS that put the logo under the clock. */
.is-native .lp-nav {
  padding-top: calc(14px + var(--safe-top));
}

/* --------------------------------------------------------------------------
   Work detail pages on a phone
   -------------------------------------------------------------------------- */
@media (max-width: 640px) {
  /* The way back stays reachable while reading a long task. Sticky rather
     than fixed: fixed would need the tab bar's height subtracting from
     everything below it, and this scrolls with the card it belongs to. */
  .wk-detail-bar {
    position: sticky;
    /* It used to stick below the top bar. The top bar scrolls away now, so
       anchoring to a height that is no longer on screen would leave it
       hanging 65px down with nothing above it. */
    top: calc(8px + var(--safe-top));
    z-index: 5;
    margin: 0 -4px 12px;
    padding: 6px 4px;
    background: var(--bg);
  }
  .wk-detail-card { padding: 14px; }
}


/* --------------------------------------------------------------------------
   Work board on a phone

   The performance table becomes cards. Seven columns at 393px is either a
   sideways scroll or seven unreadable columns, and both are worse than a stack
   of labelled rows. The labels come from data-label on each cell, so there is
   one piece of markup and one place to change it.
   -------------------------------------------------------------------------- */
@media (max-width: 720px) {
  .wk-table thead { display: none; }
  .wk-table, .wk-table tbody, .wk-table tr, .wk-table td { display: block; width: 100%; }
  .wk-table tr {
    margin-bottom: 10px; padding: 11px;
    border: 1px solid var(--border); border-radius: 12px;
    background: var(--surface-alt);
  }
  .wk-table td {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; padding: 4px 0; border: none;
  }
  .wk-table td::before {
    content: attr(data-label);
    font-size: 0.7rem; font-weight: 800; color: var(--text-muted);
    text-transform: uppercase; letter-spacing: 0.05em;
  }
  /* The member's name is the card's heading, so it keeps neither a label nor
     the side-by-side layout the numbers use. */
  .wk-table td[data-label="Member"] { display: block; margin-bottom: 6px; }
  .wk-table td[data-label="Member"]::before { content: none; }
  .wk-table .num { text-align: right; }
  .wk-table-wrap { overflow-x: visible; }

  .wk-metrics { grid-template-columns: repeat(auto-fit, minmax(94px, 1fr)); }
  .wk-metric-n { font-size: 1.35rem; }

  .wk-filters select, .wk-range input { flex: 1 1 auto; min-height: 44px; }
  .wk-range { flex: 1 1 100%; }

  /* The file row keeps its actions reachable rather than shrinking them. */
  .wk-att { flex-wrap: wrap; }
  .wk-att-actions { width: 100%; justify-content: flex-end; }
  .wk-att-act { min-height: 44px; display: inline-flex; align-items: center; padding: 0 8px; }
  .wk-att-add { min-height: 44px; }
}

/* --------------------------------------------------------------------------
   Module switch: desktop only
   -------------------------------------------------------------------------- */
@media (max-width: 768px) {
  /* Gone from the bar, because the tab bar below already is the switch: in
     Personal it ends with Work, in Work it ends with Personal, both with
     labels and a full-height target. Keeping a second copy up here would be
     two controls doing one job, competing for the tightest row on the screen
     with the refresh, the theme, the bell and the name.

     768px is not an arbitrary number: it is exactly where .bottom-nav appears,
     so the switch is handed over rather than dropped. There is no width at
     which neither has it. */
  .mode-switch { display: none; }
}

/* --------------------------------------------------------------------------
   Auth fields on a phone
   -------------------------------------------------------------------------- */
@media (max-width: 768px) {
  /* The reveal is 34px on a desktop, where a pointer is exact. On a phone it
     is a thumb target like any other and takes the same 44px minimum the
     arrows and checkboxes do -- the field has room, because the padding that
     keeps text clear of it was already reserved. */
  .field-toggle { width: 44px; height: 44px; }
  .field.has-toggle > input { padding-right: 48px !important; }
}
