/* ═══════════════════════════════════════════════════════════════════════════════
   Dexor Portal — design system.

   TWO LAYERS, and the discipline is the whole point:

     1. ATOMS own all paint. o_avatar, o_badge, o_btn, o_surface, o_kv and the
        text atoms declare every colour, radius and weight in the app, exposed
        through CSS custom properties (--av, --bdg-bg, --btn-h, --sf-radius…).

     2. COMPONENTS never re-declare paint. They set the vars they need:
              .o_m2o_avatar    { --av:38px; --av-radius:11px }
              .o_kanban_avatar { --av:32px; --av-radius:10px }
        One atom, two sizes, zero duplicated colour.

   Plus TONE CHANNELS: an ancestor sets --tone / --toneSoft / --toneLine and every
   atom inside it picks the colour up through its --tone modifier. That is how one
   rule recolours a whole kanban lane or a whole hero card, instead of a rule per
   element.

   Class names are Odoo's own web client (o_control_panel, o_data_row,
   o_form_sheet, oe_title, o_notebook_page, o-mail-*) so an Odoo developer reads
   this DOM already knowing it.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── tokens ───────────────────────────────────────────────────────────────── */
/* Every value below is the prototype's, to the digit. They were nearly right
   before and "nearly" is visible when the two are put side by side. */
:root{
  --bg:#f6f7f9; --surface:#ffffff; --surface-2:#fbfbfc;
  --sunken:#edeff3; --sunken-line:#e0e3e9;
  --line:#e6e8ec; --line-strong:#d7dae1;
  --ink:#12141a; --ink-2:#5a6170; --ink-3:#8b919e;
  --accent:#1f6f4a; --accent-ink:#fff; --accent-soft:#e8f2ec; --accent-line:#cfe6da;
  --warn:#8a6100; --warn-soft:#fdf3de; --warn-line:#efe2c2;
  --danger:#9b2c2c; --danger-soft:#fbeaea; --danger-line:#efd2d2;
  --info:#31507f; --info-soft:#eef2fb; --info-line:#dde5f5;
  --note-bg:#fdfaf0; --note-line:#e3d3ab;
  /* THE SCALE KNOB. Every size in this file that describes UI — type, padding,
     gaps, box heights, radii, the atom sizing vars — is written in rem, and 1rem
     is 16px times this number. Turn it here and the whole app resizes together;
     nothing else in the file has to know.

     Four things are deliberately NOT on it, and each is a bug if it is:
       · hairlines and shadows stay in px — a 1px rule that scales is a smudge;
       · the safe-area insets stay in device points, or the top bar walks back
         under the status bar and the home indicator eats the tab bar;
       · --tap stays 44px, because Apple's minimum touch target is an
         accessibility floor, not a style;
       · every control a caret can land in stays at an absolute 16px font-size —
         under that iOS zooms the layout on focus, and in an installed app that
         zoom sticks until relaunch. Their BOXES scale; their glyphs do not.

     .88 is the shipped value: the design was drawn against a 393pt viewport and
     read a size too large on the phone it was drawn for. */
  --ui:.88;
  --r:.875rem; --tap:44px;
  --shadow:0 1px 2px rgba(18,20,26,.05);
  --tap-bg:rgba(18,20,26,.045);
  --font:Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  /* Safe areas go through variables so they can be overridden in a test — env()
     itself cannot be simulated in a desktop browser, which is exactly why bottom
     inset bugs only ever show up on a real phone. */
  --sat-raw:env(safe-area-inset-top, 0px);
  --sab-raw:env(safe-area-inset-bottom, 0px);
  --sat:var(--sat-raw);
  --sab:var(--sab-raw);
}
@media (prefers-color-scheme:dark){
  :root{
    --bg:#0f1113; --surface:#171a1d; --surface-2:#1c2023;
    --sunken:#14171a; --sunken-line:#242a2f;
    --line:#262b30; --line-strong:#394148;
    --ink:#eef1f4; --ink-2:#a2acb8; --ink-3:#6f7985;
    --accent:#4bbd8a; --accent-ink:#08130e; --accent-soft:#14251d; --accent-line:#1e3a2c;
    --warn:#e0b45c; --warn-soft:#2a2318; --warn-line:#3d3324;
    --danger:#e58080; --danger-soft:#2b1a1a; --danger-line:#3f2626;
    --info:#8fb0e8; --info-soft:#191f2b; --info-line:#28324a;
    --note-bg:#221d12; --note-line:#3d3324;
    --shadow:0 1px 2px rgba(0,0,0,.3);
    --tap-bg:rgba(255,255,255,.05);
  }
}

/* ═══ 1. ATOMS ══════════════════════════════════════════════════════════════ */

/* o_avatar — initials / icon tile */
.o_avatar{display:grid;place-items:center;flex:0 0 auto;
  width:var(--av,2rem);height:var(--av,2rem);flex-basis:var(--av,2rem);
  border-radius:var(--av-radius,.625rem);font-size:var(--av-font,.6875rem);
  font-weight:var(--av-weight,650);letter-spacing:.01em;overflow:hidden;
  background:var(--av-bg,var(--surface-2));color:var(--av-fg,var(--ink-2));
  border:1px solid var(--av-border,var(--line))}
.o_avatar--accent {--av-bg:var(--accent-soft);--av-fg:var(--accent);--av-border:transparent}
.o_avatar--warning{--av-bg:var(--warn-soft);--av-fg:var(--warn);--av-border:var(--warn-line)}
.o_avatar--info   {--av-bg:var(--info-soft);--av-fg:var(--info);--av-border:var(--info-line)}
.o_avatar--danger {--av-bg:var(--danger-soft);--av-fg:var(--danger);--av-border:transparent}
.o_avatar--tone   {--av-bg:var(--toneSoft);--av-fg:var(--tone);--av-border:transparent}
.o_avatar--solid  {--av-bg:var(--ink);--av-fg:var(--surface);--av-border:var(--ink)}
.o_avatar--dashed {border-style:dashed;--av-fg:var(--ink-3)}
.o_avatar svg{width:var(--av-icon,.9375rem);height:var(--av-icon,.9375rem);stroke:currentColor;fill:none;
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}

/* o_badge — status / meta pill */
.o_badge{display:inline-flex;align-items:center;gap:.3125rem;flex:0 0 auto;white-space:nowrap;
  font-size:var(--bdg-font,.65625rem);font-weight:var(--bdg-weight,650);
  padding:var(--bdg-pad,2px .4375rem);border-radius:var(--bdg-radius,.375rem);
  background:var(--bdg-bg,var(--surface-2));color:var(--bdg-fg,var(--ink-2));
  border:1px solid var(--bdg-border,transparent);max-width:100%;overflow:hidden;text-overflow:ellipsis}
.o_badge--success{--bdg-bg:var(--accent-soft);--bdg-fg:var(--accent)}
.o_badge--warning{--bdg-bg:var(--warn-soft);--bdg-fg:var(--warn)}
.o_badge--danger {--bdg-bg:var(--danger-soft);--bdg-fg:var(--danger)}
.o_badge--info   {--bdg-bg:var(--info-soft);--bdg-fg:var(--info)}
.o_badge--neutral{--bdg-bg:var(--surface-2);--bdg-fg:var(--ink-3);--bdg-border:var(--line)}
.o_badge--outline{--bdg-bg:var(--surface-2);--bdg-fg:var(--ink-2);--bdg-border:var(--line)}
.o_badge--tone   {--bdg-bg:var(--toneSoft);--bdg-fg:var(--tone)}
.o_badge--glass  {--bdg-bg:var(--surface);--bdg-border:var(--toneLine);--bdg-radius:.5rem;--bdg-pad:3px .5rem}
.o_badge--caps   {--bdg-font:.59375rem;--bdg-weight:700;--bdg-radius:.3125rem;--bdg-pad:2px .3125rem;
  letter-spacing:.05em;text-transform:uppercase}
.o_badge--count  {--bdg-radius:999px;--bdg-pad:1px .375rem;--bdg-font:.625rem;--bdg-weight:700;
  min-width:1.1875rem;justify-content:center}
.o_badge svg{width:.6875rem;height:.6875rem;stroke:currentColor;fill:none;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}

/* o_btn — every clickable */
.o_btn{display:inline-flex;align-items:center;justify-content:center;gap:.375rem;cursor:pointer;
  font:inherit;font-size:var(--btn-font,.8125rem);font-weight:var(--btn-weight,600);
  height:var(--btn-h,2.125rem);min-height:var(--btn-h,2.125rem);padding:var(--btn-pad,0 .75rem);
  border-radius:var(--btn-radius,.625rem);
  background:var(--btn-bg,var(--surface-2));color:var(--btn-fg,var(--ink-2));
  border:1px solid var(--btn-border,var(--line));
  transition:background .15s,color .15s,border-color .15s,transform .12s}
.o_btn:active{background:var(--tap-bg)}
.o_btn:disabled{opacity:.45}
.o_btn--primary{--btn-bg:var(--accent);--btn-fg:var(--accent-ink);--btn-border:var(--accent)}
.o_btn--ghost  {--btn-bg:var(--surface);--btn-fg:var(--ink)}
.o_btn--subtle {--btn-bg:var(--surface-2);--btn-fg:var(--ink)}
.o_btn--plain  {--btn-bg:transparent;--btn-border:transparent}
.o_btn--accent {--btn-bg:var(--accent-soft);--btn-fg:var(--accent);--btn-border:transparent}
.o_btn--danger {--btn-bg:var(--surface);--btn-fg:var(--danger);--btn-border:var(--line)}
.o_btn--dashed {--btn-bg:transparent;--btn-border:var(--line-strong);border-style:dashed;--btn-fg:var(--ink-3)}
.o_btn--icon   {--btn-pad:0;width:var(--btn-h,2.125rem);flex:0 0 var(--btn-h,2.125rem)}
.o_btn--round  {--btn-radius:50%}
.o_btn--block  {flex:1;width:100%}
.o_btn svg{width:var(--btn-icon,1rem);height:var(--btn-icon,1rem);stroke:currentColor;fill:none;
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}

/* o_surface — any card / panel */
.o_surface{background:var(--sf-bg,var(--surface));border:1px solid var(--sf-border,var(--line));
  border-radius:var(--sf-radius,var(--r));box-shadow:var(--sf-shadow,var(--shadow));overflow:hidden}
.o_surface--tinted{--sf-bg:var(--surface-2)}
.o_surface--flat  {--sf-shadow:none}
.o_surface--dashed{--sf-bg:transparent;--sf-border:var(--line-strong);--sf-shadow:none;border-style:dashed}

/* o_kv — label + value pair */
.o_kv{display:flex;justify-content:space-between;align-items:center;gap:.875rem}
.o_kv_label{font-size:var(--kv-k,.8125rem);color:var(--ink-2);flex:0 0 auto}
.o_kv_value{font-size:var(--kv-v,.84375rem);font-weight:570;letter-spacing:-.012em;
  font-variant-numeric:tabular-nums;text-align:right;min-width:0}
.o_kv--stacked{display:block}
.o_kv--stacked > .o_kv_label{display:block;font-size:var(--kv-k,.59375rem);font-weight:650;
  letter-spacing:.07em;text-transform:uppercase;color:var(--ink-3)}
.o_kv--stacked > .o_kv_value{display:block;text-align:left;margin-top:var(--kv-gap,3px)}

/* text atoms */
.o_caption{font-size:var(--cap,.65625rem);font-weight:650;letter-spacing:.06em;
  text-transform:uppercase;color:var(--cap-fg,var(--ink-3))}
.o_title{font-size:var(--ttl,.875rem);font-weight:var(--ttl-w,600);letter-spacing:-.015em}
.o_muted{font-size:var(--mut,.75rem);color:var(--ink-2)}
.o_muted--soft{color:var(--ink-3)}
.o_amount,.num{font-size:var(--amt,.875rem);font-weight:var(--amt-w,640);letter-spacing:-.018em;
  font-variant-numeric:tabular-nums;font-feature-settings:'tnum' 1}
.o_currency{font-size:var(--cur,.6875rem);font-weight:650;color:var(--ink-3);letter-spacing:.05em}
.o_ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}

/* misc atoms */
.o_dot{width:var(--dot,.375rem);height:var(--dot,.375rem);flex:0 0 var(--dot,.375rem);border-radius:50%;
  background:var(--dot-bg,currentColor)}
.o_caret{width:var(--crt,.9375rem);height:var(--crt,.9375rem);flex:0 0 var(--crt,.9375rem);
  stroke:var(--ink-3);fill:none;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.o_progress{height:var(--pg,.375rem);border-radius:.25rem;background:var(--surface-2);
  border:1px solid var(--line);overflow:hidden}
.o_progress_fill{display:block;height:100%;border-radius:.25rem;background:var(--pg-fill,var(--accent))}
.o_divider{height:1px;background:var(--line)}

/* ═══ 2. APP SHELL ══════════════════════════════════════════════════════════
   Everything below the tokens is new paint; everything in THIS section is not.
   The fixed body, the --sat/--sab variables, the ::after backstop and the
   visualViewport keyboard rule were each paid for on a real iPhone and are kept
   verbatim. The prototype this design comes from scrolls the document and sizes
   with 100dvh — on an installed iOS PWA that is the dead strip below the tab bar
   and the white slab under the keyboard. */

/* manipulation = pan and pinch, but NO double-tap zoom. Zero specificity, so the
   scroller's own pan-y still wins where it is declared. */
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent;touch-action:manipulation}
/* The document must be physically unable to scroll, or iOS drags fixed children
   with the rubber-band. overscroll-behavior alone is not enough. */
/* text-size-adjust:100% turns off WebKit's text autosizing — the "font boosting"
   that silently enlarges type inside blocks it judges too wide to read, without
   touching the boxes around it. It is on by default on iOS and it is the reason a
   layout that measures correctly can still look magnified on a phone and nowhere
   else. Nothing here wants it: every size in this file was chosen against a real
   393pt viewport, so a device second-guessing them can only be wrong. */
/* The root size every rem below is measured from — and the only line that reads
   --ui. 16px is the size the design was drawn at, so --ui:1 reproduces it to the
   digit; the file below is a faithful conversion, not a redraw. */
html{height:100%;overflow:hidden;overscroll-behavior:none;
  font-size:calc(16px * var(--ui,1));
  -webkit-text-size-adjust:100%;text-size-adjust:100%}
body{position:fixed;inset:0;width:100%;height:100%;overflow:hidden;overscroll-behavior:none;
  margin:0;background:var(--surface);color:var(--ink);font:.9375rem/1.45 var(--font);
  -webkit-font-smoothing:antialiased;font-variant-numeric:tabular-nums}
/* Anything under 16px makes iOS zoom on focus and shifts the layout viewport. */
input,select,textarea{font:inherit;font-size:16px;color:inherit}
button{font:inherit;color:inherit}

/* bottom:0 on a fixed element is the TRUE viewport bottom under viewport-fit=cover,
   including the home-indicator inset. If any region below the bar is inside the web
   view it becomes part of the bar instead of a grey gap. */
body::after{content:'';position:fixed;left:0;right:0;bottom:0;height:50vh;
  background:var(--surface);z-index:-1;pointer-events:none}
.app{position:fixed;top:0;left:0;right:0;bottom:0;display:flex;flex-direction:column}
/* Only while a real keyboard is open do we pin an explicit height instead. */
.app.kbd{top:var(--vvt,0px);bottom:auto;height:var(--vvh)}
.scroll{flex:1;min-height:0;background:var(--bg);overflow-y:auto;overflow-x:hidden;
  -webkit-overflow-scrolling:touch;overscroll-behavior:none;touch-action:pan-y;position:relative}

/* ── control panel ── */
.o_control_panel{flex:none;background:var(--surface);border-bottom:1px solid var(--line);
  padding:calc(var(--sat) + .625rem) .875rem 0}
.o_cp_top{display:flex;align-items:center;gap:.5625rem;min-height:2.5rem}
.o_cp_top h1{font-size:1.1875rem;font-weight:650;letter-spacing:-.02em;margin:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.o_pager{font-size:.75rem;color:var(--ink-3);font-weight:500;flex:1;white-space:nowrap}
.o_cp_top .o_btn{--btn-h:2.125rem;--btn-icon:1.0625rem}

.o_view_switcher{display:flex;padding:2px;gap:2px;background:var(--surface-2);
  border:1px solid var(--line);border-radius:.625rem;flex:none}
.o_view_switcher button{width:2.125rem;height:1.75rem;border:0;border-radius:.5rem;background:transparent;
  color:var(--ink-3);display:grid;place-items:center;cursor:pointer;transition:background .15s,color .15s}
.o_view_switcher button svg{width:1rem;height:1rem;stroke:currentColor;fill:none;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round}
.o_view_switcher button[aria-pressed=true]{background:var(--surface);color:var(--ink);box-shadow:var(--shadow)}

.o_searchview{position:relative;margin:.625rem 0 .75rem}
.o_searchview > svg{position:absolute;left:.6875rem;top:50%;transform:translateY(-50%);
  width:1rem;height:1rem;stroke:var(--ink-3);fill:none;stroke-width:1.8;stroke-linecap:round;pointer-events:none}
.o_searchview input{width:100%;height:2.375rem;padding:0 2.25rem;border:1px solid var(--line);border-radius:.625rem;
  background:var(--surface-2);color:var(--ink);outline:none;transition:border-color .15s,background .15s}
.o_searchview input::placeholder{color:var(--ink-3)}
.o_searchview input:focus{border-color:var(--accent);background:var(--surface)}
.o_searchview_clear{position:absolute;right:.4375rem;top:50%;transform:translateY(-50%);
  --btn-h:1.5rem;--btn-radius:50%;--btn-bg:var(--line);--btn-border:transparent;--btn-font:.875rem;line-height:1}

.o_searchview_facets{display:flex;gap:.4375rem;overflow-x:auto;padding-bottom:.6875rem;scrollbar-width:none}
.o_searchview_facets::-webkit-scrollbar{display:none}
.o_searchview_facet{flex:0 0 auto;--btn-h:1.875rem;--btn-pad:0 .75rem;--btn-radius:999px;--btn-font:.8125rem;--btn-weight:500}
.o_searchview_facet .o_chip_count{font-size:.71875rem;color:var(--ink-3);font-weight:550}
.o_searchview_facet[aria-selected=true]{--btn-bg:var(--ink);--btn-border:var(--ink);--btn-fg:var(--surface)}
.o_searchview_facet[aria-selected=true] .o_chip_count{color:var(--surface);opacity:.7}

/* ── bottom nav ── */
.o_bottom_nav{flex:none;display:flex;flex-wrap:wrap;background:var(--surface);
  border-top:1px solid var(--line);padding-top:calc(var(--sab) / 2)}
/* max(--tap, …) is the pattern used on every hit area in this file: the box is a
   rem so it rides the scale down, and the 44px floor underneath it means it can
   never ride down past the smallest target a finger can be asked to find. At
   --ui:1 the rem wins and the design is unchanged; at .88 the floor takes over. */
.o_bottom_nav button{flex:1;min-height:max(var(--tap),3.125rem);border:0;background:none;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:.25rem;font-size:.6875rem;font-weight:550;color:var(--ink-3);cursor:pointer}
.o_bottom_nav button svg{width:1.375rem;height:1.375rem;stroke:currentColor;fill:none;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round}
.o_bottom_nav button[aria-selected=true]{color:var(--accent);font-weight:620}
/* Half the home-indicator reserve. The other half is padding-top, so the labels sit
   optically centred in the bar instead of pinned above a void — while the tap target
   still stops clear of the indicator, as Apple requires. */
.o_bottom_nav::after{content:'';flex:0 0 100%;height:calc(var(--sab) / 2);background:var(--surface)}
.o_bottom_nav .dotmark{width:1.375rem;height:1.375rem;border-radius:.375rem;border:1.6px solid currentColor;opacity:.85}
.inset-only{flex:none;height:var(--sab);background:var(--bg)}

/* ── the new-record button ──
   A sibling of the scroller, not a child of it: inside .scroll it would slide
   away with the content. .app is fixed, so absolute positions against it. */
/* Measured against the SCROLLER's bottom edge, not against the tab bar's height:
   the bar, the record action bar and the banners are all flex siblings, so
   whatever is down there, this clears it without knowing what it is. */
.o_button_new{position:absolute;right:1rem;bottom:var(--fab-bottom, 4.5rem);z-index:25;
  --btn-h:max(var(--tap),3rem);--btn-radius:.9375rem;--btn-icon:1.25rem;
  box-shadow:0 8px 20px -6px rgba(31,111,74,.5)}
.o_button_new:active{transform:scale(.94)}

/* ═══ 3. LIST VIEW ══════════════════════════════════════════════════════════ */
.o_list_view{padding-bottom:1.625rem}
.o_group_header{padding:1rem 1rem .4375rem;font-size:.71875rem;font-weight:650;letter-spacing:.06em;
  text-transform:uppercase;color:var(--ink-3);position:sticky;top:0;background:var(--bg);z-index:5}
.o_list_table{list-style:none;margin:0 .75rem;padding:0;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow);overflow:hidden}
.o_list_view .o_data_row{display:flex;align-items:center;gap:.75rem;padding:.75rem .8125rem;width:100%;
  min-height:var(--tap);
  cursor:pointer;background:var(--surface);border:0;border-bottom:1px solid var(--line);
  text-align:left;transition:background .12s}
.o_list_table .o_data_row:last-child{border-bottom:0}
.o_list_view .o_data_row:active{background:var(--tap-bg)}
.o_m2o_avatar{--av:2.375rem;--av-radius:.6875rem;--av-font:.8125rem;--av-weight:600}
.o_data_cell{flex:1 1 auto;min-width:0}
.o_list_line_primary{display:flex;align-items:baseline;gap:.5rem}
.o_list_title{font-size:.90625rem;font-weight:570;letter-spacing:-.01em;color:var(--ink);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1 1 auto}
.o_list_monetary{font-size:.875rem;font-weight:620;letter-spacing:-.01em;flex:0 0 auto;
  font-variant-numeric:tabular-nums}
.o_list_line_secondary{display:flex;align-items:center;gap:.375rem;margin-top:2px}
.o_list_subtitle{font-size:.75rem;color:var(--ink-2);white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis;flex:1 1 auto}
.o_list_time{font-size:.71875rem;color:var(--ink-3);flex:0 0 auto}
.o_state_badge{--bdg-font:.65625rem;--bdg-weight:600;--bdg-pad:2px .375rem;letter-spacing:.01em}
.o_unread_dot{--dot:.375rem;--dot-bg:var(--accent)}
.o_unread_dot.hidden{visibility:hidden}
.o_unread_dot.hot{--dot-bg:var(--warn)}

/* ═══ 4. KANBAN VIEW ════════════════════════════════════════════════════════ */
.o_kanban_renderer{display:flex;gap:.6875rem;padding:.875rem .75rem 1.625rem;overflow-x:auto;
  scroll-snap-type:x mandatory;scrollbar-width:none;align-items:flex-start}
.o_kanban_renderer::-webkit-scrollbar{display:none}
/* A lane is a real container: tinted body, its own header, its own tone. */
.o_kanban_group{flex:0 0 87%;scroll-snap-align:center;display:flex;flex-direction:column;gap:.5625rem;
  background:var(--sunken);border:1px solid var(--sunken-line);border-radius:.9375rem;padding:.6875rem .625rem;
  --tone:var(--ink-3);--toneSoft:var(--surface-2);--toneLine:var(--line)}
/* The tone channel. One class on the lane recolours its dot, title, count badge
   and every card avatar inside it — the atoms read --tone, nothing else changes. */
.tone-success{--tone:var(--accent);--toneSoft:var(--accent-soft);--toneLine:var(--accent-line)}
.tone-warning{--tone:var(--warn);--toneSoft:var(--warn-soft);--toneLine:var(--warn-line)}
.tone-danger {--tone:var(--danger);--toneSoft:var(--danger-soft);--toneLine:var(--danger-line)}
.tone-info   {--tone:var(--info);--toneSoft:var(--info-soft);--toneLine:var(--info-line)}
.tone-neutral{--tone:var(--ink-3);--toneSoft:var(--surface-2);--toneLine:var(--line)}
.o_kanban_header{display:flex;align-items:center;gap:.5rem;padding:1px 3px 3px}
.o_column_dot{--dot:.4375rem;--dot-bg:var(--tone)}
.o_column_title{font-size:.78125rem;font-weight:680;color:var(--tone);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.o_column_count{--bdg-pad:1px .4375rem;line-height:1rem}
.o_column_total{margin-left:auto;font-size:.6875rem;font-weight:640;color:var(--ink-3);
  font-variant-numeric:tabular-nums;flex:none}
.o_kanban_record{--sf-radius:.75rem;padding:.6875rem .75rem .625rem;cursor:pointer;width:100%;min-width:0;
  max-width:100%;text-align:left;transition:transform .12s,box-shadow .15s}
.o_kanban_record:active{transform:scale(.985)}
.o_kanban_record_top{display:flex;align-items:center;gap:.5625rem}
.o_kanban_avatar{--av:2rem;--av-radius:.625rem;--av-font:.6875rem;--av-weight:680}
.o_kanban_record_headings{flex:1;min-width:0}
.o_kanban_record_title{display:block;font-size:.84375rem;font-weight:620;letter-spacing:-.015em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.o_kanban_record_subtitle{display:block;font-size:.6875rem;color:var(--ink-3);margin-top:2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.o_kanban_record_amount{display:flex;align-items:baseline;gap:.3125rem;margin-top:.6875rem}
.o_kanban_amount_value{--amt:1.125rem;--amt-w:660;letter-spacing:-.025em}
.o_kanban_tags{display:flex;gap:.3125rem;margin-top:.5rem;flex-wrap:wrap}
.o_kanban_record_bottom{display:flex;align-items:center;gap:.4375rem;margin-top:.6875rem;padding-top:.5625rem;
  border-top:1px solid var(--line)}
.o_kanban_due{--bdg-radius:.4375rem;--bdg-pad:3px .5rem}
.o_kanban_user{--av:1.5rem;--av-radius:.5rem;--av-font:.59375rem;--av-weight:680;margin-left:auto}
.o_kanban_group_empty{--sf-radius:.6875rem;font-size:.71875rem;color:var(--ink-3);text-align:center;padding:1.25rem 0}

/* ═══ 5. FORM VIEW ══════════════════════════════════════════════════════════ */
.o_form_control_panel{flex:none;display:flex;align-items:center;gap:.625rem;
  padding:calc(var(--sat) + .5625rem) .75rem .5625rem;background:var(--surface);border-bottom:1px solid var(--line)}
.o_form_control_panel .o_cp_back{--btn-h:2.125rem;--btn-icon:1rem}
.o_breadcrumb{flex:1;min-width:0;display:flex;align-items:center;gap:.4375rem}
.o_breadcrumb_ref{font-size:.90625rem;font-weight:640;letter-spacing:-.01em;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Form actions in the top bar — Odoo's dirty state on a phone. [hidden] alone
   loses to display:flex, so the override is explicit. */
.tb-act{flex:none;display:flex;gap:.5rem}
.tb-act[hidden],.o_btn[hidden]{display:none}
.tb-act .o_btn{--btn-h:2.125rem;--btn-font:.84375rem;--btn-weight:620;--btn-bg:var(--surface);--btn-fg:var(--ink)}
/* Shown only while the record is dirty. */
.whendirty{display:none}
.dirty .whendirty{display:block}

/* the rail */
.o_form_statusbar{background:var(--surface);padding:.9375rem 1.25rem .8125rem;border-bottom:1px solid var(--line)}
.o_statusbar_track{display:flex;align-items:center}
.o_statusbar_node{width:.6875rem;height:.6875rem;border-radius:50%;flex:0 0 .6875rem;background:var(--surface);
  border:2px solid var(--line-strong);position:relative}
.o_statusbar_node.done{background:var(--accent);border-color:var(--accent)}
.o_statusbar_node.cur{background:var(--surface);border-color:var(--accent);box-shadow:0 0 0 4px var(--accent-soft)}
.o_statusbar_node.cur::after{content:'';position:absolute;inset:2px;border-radius:50%;background:var(--accent)}
.o_statusbar_segment{flex:1;height:2px;background:var(--line);border-radius:2px}
.o_statusbar_segment.done{background:var(--accent)}
.o_statusbar_labels{display:flex;margin-top:.5rem}
.o_statusbar_labels span{flex:1;min-width:0;font-size:.625rem;font-weight:600;letter-spacing:.04em;
  text-transform:uppercase;color:var(--ink-3);text-align:center;overflow:hidden;text-overflow:ellipsis}
.o_statusbar_labels span:first-child{text-align:left}
.o_statusbar_labels span:last-child{text-align:right}
.o_statusbar_labels span.on{color:var(--ink)}

/* the sheet */
.o_form_sheet{background:var(--surface);padding:1rem .875rem 0;border-bottom:1px solid var(--line)}
.oe_title{display:flex;gap:.75rem;align-items:center}
.oe_title_avatar{--av:2.75rem;--av-radius:.875rem;--av-font:.9375rem;--av-weight:680}
.oe_title_text{min-width:0;flex:1}
.oe_title_name{font-size:1.09375rem;font-weight:660;letter-spacing:-.026em;line-height:1.22}
.oe_title_meta{display:flex;align-items:center;gap:.4375rem;margin-top:.375rem;flex-wrap:wrap}
.o_user_badge{display:inline-flex;align-items:center;gap:.3125rem;min-width:0}
.o_user_badge .o_avatar{--av:1.125rem;--av-radius:.375rem;--av-font:.5rem;--av-weight:700}
.o_user_badge .o_user_name{font-size:.6875rem;color:var(--ink-3);font-weight:550;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* the hero */
.o_monetary_summary{margin:1.125rem 0 .25rem;padding:.875rem .9375rem;border-radius:.875rem;display:flex;align-items:center;
  gap:.875rem;background:var(--toneSoft);border:1px solid var(--toneLine);
  --tone:var(--ink-3);--toneSoft:var(--surface-2);--toneLine:var(--line)}
.o_monetary_summary.tone-success{--tone:var(--accent);--toneSoft:var(--accent-soft);--toneLine:var(--accent-line)}
.o_monetary_summary.tone-warning{--tone:var(--warn);--toneSoft:var(--warn-soft);--toneLine:var(--warn-line)}
.o_monetary_summary.tone-danger {--tone:var(--danger);--toneSoft:var(--danger-soft);--toneLine:var(--danger-line)}
.o_monetary_summary.tone-info   {--tone:var(--info);--toneSoft:var(--info-soft);--toneLine:var(--info-line)}
.o_monetary_main{flex:1;min-width:0}
.o_monetary_label{display:flex;align-items:center;gap:.375rem;font-size:.59375rem;font-weight:700;letter-spacing:.1em;
  text-transform:uppercase;color:var(--tone);margin-bottom:.5625rem}
.o_monetary_label::before{content:'';width:.875rem;height:2px;border-radius:2px;background:var(--tone);opacity:.55}
.o_monetary_value{display:flex;align-items:baseline;gap:.375rem}
.o_monetary_amount{--amt:1.75rem;--amt-w:620;letter-spacing:-.024em;line-height:1.05}
.o_monetary_currency{font-size:.75rem;font-weight:700;color:var(--tone);letter-spacing:.06em}
.o_monetary_breakdown{display:flex;flex-wrap:wrap;gap:.375rem;margin-top:.6875rem}
.o_breakdown_chip{align-items:baseline}
.o_breakdown_label{font-size:.53125rem;font-weight:700;letter-spacing:.09em;text-transform:uppercase;
  color:var(--tone);opacity:.9}
.o_breakdown_value{font-size:.71875rem;font-weight:640;letter-spacing:-.01em;color:var(--ink);
  font-variant-numeric:tabular-nums}
.o_progress_ring{flex:0 0 3.875rem;text-align:center}
.o_progress_ring svg{width:3.375rem;height:3.375rem;transform:rotate(-90deg);display:block;margin:0 auto}
.o_ring_track{fill:none;stroke:var(--toneLine);stroke-width:5}
.o_ring_arc{fill:none;stroke:var(--tone);stroke-width:5;stroke-linecap:round}
.o_ring_value{font-size:.65625rem;font-weight:700;fill:var(--tone);transform:rotate(90deg);
  transform-origin:center;text-anchor:middle;font-variant-numeric:tabular-nums}
.o_ring_label{font-size:.53125rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--tone);margin-top:.3125rem;opacity:.85}

/* the meta band — tinted, inset, dot-separated */
.o_meta_group{display:flex;align-items:stretch;margin:1rem 0;padding:.625rem 2px;
  background:var(--sunken);border:1px solid var(--sunken-line);border-radius:.75rem}
.o_meta_group > div{flex:1;min-width:0;padding:2px .6875rem;position:relative}
.o_meta_group > div + div::before{content:'';position:absolute;left:-1px;top:50%;margin-top:-1.5px;
  width:3px;height:3px;border-radius:50%;background:var(--line-strong)}
.o_meta_group .o_kv_label{display:flex;align-items:center;gap:.3125rem;--kv-k:.59375rem;line-height:1}
/* One line, always. A label that wraps pushes its own value down and breaks the
   baseline the strip is read along. */
.o_meta_group .o_kv_label span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.o_meta_group .o_kv_label svg{width:.75rem;height:.75rem;flex:0 0 .75rem;stroke:currentColor;fill:none;
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.o_meta_group .o_kv_value{--kv-v:.84375rem;font-weight:640;margin-top:.375rem;line-height:1.15;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* the notebook */
/* The tab is a 44px hit area and the strip around it is thinner by exactly what
   the tab gained, so the notebook header still measures what it did — the target
   grew, the chrome did not. */
.o_notebook_headers{display:flex;gap:.375rem;padding:.25rem .75rem;background:var(--surface);
  border-bottom:1px solid var(--line);position:sticky;top:0;z-index:6;overflow-x:auto;scrollbar-width:none}
.o_notebook_headers::-webkit-scrollbar{display:none}
.o_notebook_tab{display:inline-flex;align-items:center;gap:.375rem;flex:none;border:1px solid transparent;
  background:transparent;font-size:.8125rem;font-weight:570;color:var(--ink-2);padding:.4375rem .6875rem;
  min-height:var(--tap);border-radius:.625rem;cursor:pointer;transition:background .15s,color .15s}
.o_notebook_tab:active{background:var(--tap-bg)}
.o_notebook_tab[aria-selected=true]{background:var(--accent);border-color:var(--accent);
  color:var(--accent-ink);font-weight:620}
.o_notebook_tab .o_notebook_count{--bdg-border:var(--line);--bdg-fg:var(--ink-3);line-height:.9375rem}
.o_notebook_tab[aria-selected=true] .o_notebook_count{--bdg-bg:rgba(255,255,255,.22);
  --bdg-fg:var(--accent-ink);--bdg-border:transparent}
.o_notebook_page{padding:.875rem .75rem 1.625rem}

/* section heading sits on the canvas, not in a bar */
.o_horizontal_separator{font-size:.65625rem;font-weight:650;letter-spacing:.07em;text-transform:uppercase;
  color:var(--ink-3);margin:.25rem 2px .5rem}
.o_horizontal_separator:not(:first-child){margin-top:1.25rem}
.o_group{margin-bottom:.75rem}

/* Definition rows — label left, value right, one line each.

   Editable and read-only are the SAME row. A field you may change is not a
   different kind of thing from one you may not, and drawing it as a boxed input
   with a caps label above doubled every row's height: the form that fitted on
   one screen in the design needed two and a half here. The control is borderless
   and right-aligned, so at rest it is the value; the row tints on focus. */
/* The row IS the tap target — tapping it is what puts the control on screen — so
   it keeps the 44px floor whatever the scale says. */
.o_field_row{padding:.6875rem .8125rem;min-height:var(--tap);border-bottom:1px solid var(--line);--kv-v:.84375rem;gap:.75rem}
.o_group > .o_field_row:last-child,.o_group > .o_field_block:last-child,
.o_group > .o_field_contact:last-child{border-bottom:0}
.o_field_row > .o_kv_label{flex:0 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.o_field_row > .o_kv_value{flex:1 1 auto;word-break:break-word}
.o_field_row .o_kv_value.mut{color:var(--ink-3);font-weight:500}
.o_field_row .o_kv_value.warnv{color:var(--warn)}
.o_field_row .o_kv_value.badv{color:var(--danger)}
.o_field_row .o_kv_value.okv{color:var(--accent)}
.o_field_required > .o_kv_label::after{content:' *';color:var(--danger)}

/* The control is 16px — the size below which iOS zooms the layout on focus, a
   zoom that STICKS in a standalone PWA. It is only ever on screen while the row
   is being edited, so the resting size is still the design's 13.5px text. */
.o_field_row input,.o_field_row select{width:100%;border:0;background:transparent;padding:0;margin:0;
  min-height:0;height:auto;font:inherit;font-size:16px;font-weight:570;
  letter-spacing:-.012em;color:var(--ink);text-align:right;font-variant-numeric:tabular-nums;
  outline:none;appearance:none;-webkit-appearance:none;border-radius:0}
.o_field_row .o_field_input,.o_field_contact .o_field_input{display:none}
.o_field_row.editing .o_field_input{display:block}
.o_field_row.editing .o_field_display{display:none}
.o_field_contact.editing .o_field_input{display:block;width:100%;border:0;background:transparent;
  padding:0;font:inherit;font-size:16px;font-weight:550;color:var(--ink);outline:none;text-align:left}
.o_field_contact.editing .o_field_display{display:none}
.o_field_tap{cursor:text}
.o_field_row select{direction:rtl}          /* right-aligns the option text too */
.o_field_row input::placeholder{color:var(--ink-3);font-weight:500}
.o_field_row input::-webkit-calendar-picker-indicator,
.o_field_row input::-webkit-inner-spin-button{display:none;-webkit-appearance:none}
.o_field_row:focus-within{background:var(--accent-soft)}
.o_field_row:focus-within > .o_kv_label{color:var(--accent)}
.o_field_row input[type=checkbox]{width:1.25rem;height:1.25rem;flex:none;accent-color:var(--accent)}
.o_field_row .o_field_stars{justify-content:flex-end}

/* The two that cannot be a single line: long text, and a set of tags. */
.o_field_block{padding:.6875rem .8125rem;border-bottom:1px solid var(--line)}
.o_field_block > .o_kv_label{--kv-k:.59375rem}
/* 16px for the reason the row above gives, but the trade is not the same one.
   These controls are boxes that stay on screen whether or not they have focus, so
   there is no resting state to hide the size in and the glyphs really are bigger.
   The LINE is not: 13.5px at the inherited 1.45 and 16px at 1.25 are both a 20px
   line box, so the textarea holds the same three-and-a-bit lines inside its 84px
   and the chip input lands within half a point of the height it had. Density is a
   rhythm, not a point size, and the rhythm is what the design was specifying. */
.o_field_block textarea,.o_field_block .chipadd{width:100%;border:1px solid var(--line);
  border-radius:.5625rem;padding:.5625rem .6875rem;background:var(--surface-2);outline:none;
  font-size:16px;line-height:1.25;transition:border-color .15s,background .15s}
.o_field_block textarea{min-height:5.25rem;resize:vertical}
.o_field_block textarea:focus,.o_field_block .chipadd:focus{border-color:var(--accent);background:var(--surface)}

.o_field_contact{display:flex;align-items:center;gap:.6875rem;padding:.6875rem .8125rem;
  min-height:var(--tap);border-bottom:1px solid var(--line)}
.o_field_contact_text{flex:1;min-width:0}
.o_field_contact_text .o_kv_label{--kv-k:.59375rem}
.o_field_contact_text .o_kv_value{--kv-v:.84375rem;font-weight:550;margin-top:1px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

.o_field_contact:focus-within{background:var(--accent-soft)}
.o_field_contact:focus-within .o_kv_label{color:var(--accent)}
.o_field_button{--btn-h:2rem;--btn-icon:.9375rem}

/* chips (many2many) and stars (priority) */
.o_field_tags{display:flex;flex-wrap:wrap;gap:.375rem;margin-bottom:.5rem}
.o_tag{--bdg-font:.75rem;--bdg-weight:560;--bdg-pad:.25rem .375rem .25rem .5625rem;--bdg-bg:var(--accent-soft);
  --bdg-fg:var(--accent);--bdg-radius:.4375rem}
.o_tag button{border:0;background:none;color:inherit;font-size:.9375rem;line-height:1;padding:0 2px;
  cursor:pointer;opacity:.75}
.o_field_stars{display:flex;gap:.25rem}
.o_field_stars button{border:0;background:none;font-size:1.25rem;line-height:1;cursor:pointer;
  color:var(--line-strong);padding:2px}
.o_field_stars button.on{color:var(--warn)}

/* x2many — child records, read-only */
.o_field_x2many .o_x2m_toolbar{display:flex;align-items:center;gap:.5625rem;padding:.625rem .75rem;
  border-bottom:1px solid var(--line);background:var(--surface-2)}
.o_x2m_toolbar_label{flex:1;font-size:.65625rem;font-weight:650;letter-spacing:.06em;
  text-transform:uppercase;color:var(--ink-3)}
.o_field_x2many .o_data_row{display:flex;align-items:center;gap:.6875rem;padding:.6875rem .75rem;
  min-height:var(--tap);border-bottom:1px solid var(--line)}
.o_field_x2many .o_data_row:last-child{border-bottom:0}
.o_field_x2many .o_data_row_icon{--av:2.125rem;--av-radius:.625rem}
.o_field_x2many .o_data_row_text{flex:1;min-width:0}
.o_field_x2many .o_data_row_name{font-size:.84375rem;font-weight:570;letter-spacing:-.012em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.o_field_x2many .o_data_row_meta{display:flex;align-items:center;gap:.4375rem;margin-top:.25rem}

/* footer action bar */
.o_form_buttons{flex:none;display:flex;gap:.5625rem;padding:.625rem .75rem calc(.625rem + var(--sab));
  background:var(--surface);border-top:1px solid var(--line)}
.o_form_buttons .o_btn{--btn-h:max(var(--tap),2.875rem);--btn-radius:.75rem;--btn-font:.90625rem;--btn-weight:600}
.o_form_buttons .o_btn--icon{flex:0 0 max(var(--tap),2.875rem);width:max(var(--tap),2.875rem)}

/* ═══ 6. CHATTER ════════════════════════════════════════════════════════════
   One thread, not three tabs. The composer's mode decides what a post BECOMES;
   it does not filter what you can see. A note you wrote and the customer's reply
   are the same conversation and belong on the same screen — which is what the
   design does, and what Odoo's own chatter does. */
.o-mail-Chatter{padding:0}

/* followers + activity summary */
.o-mail-Followers{padding:.75rem .8125rem}
.o-mail-Followers_row{display:flex;align-items:center;gap:.625rem}
.o-mail-Followers_avatars{display:flex}
.o-mail-Followers_avatars .o_avatar{--av:1.625rem;--av-radius:.5rem;--av-font:.59375rem;margin-left:-6px;
  --av-bg:var(--surface);--av-border:var(--line-strong)}
.o-mail-Followers_avatars .o_avatar:first-child{margin-left:0}
.o-mail-Followers_text{flex:1;font-size:.75rem;color:var(--ink-2);line-height:1.3;min-width:0}
.o-mail-Followers_text .o_value_muted{color:var(--ink-3)}
.o-mail-Followers_button{--btn-h:1.75rem;--btn-pad:0 .6875rem;--btn-radius:.5rem;--btn-font:.75rem}
.o-mail-ActivitySummary{display:flex;gap:.375rem;margin-top:.6875rem;flex-wrap:wrap}
.o-mail-ActivityChip{--bdg-font:.6875rem;--bdg-weight:600;--bdg-pad:3px .5rem;--bdg-radius:.4375rem}
.o-mail-ActivityChip .o_dot{--dot:.3125rem}
.o-mail-ActivityChip.od{--bdg-bg:var(--danger-soft);--bdg-fg:var(--danger)}
.o-mail-ActivityChip.td{--bdg-bg:var(--warn-soft);--bdg-fg:var(--warn)}
.o-mail-ActivityChip.pl{--bdg-bg:var(--surface-2);--bdg-fg:var(--ink-2);--bdg-border:var(--line)}

/* composer */
.o-mail-Composer{overflow:visible;position:relative;margin-bottom:.75rem}
.o-mail-Chatter_topbar{display:flex;border-bottom:1px solid var(--line);background:var(--surface-2)}
.o-mail-Chatter_topbar button{flex:1;border:0;background:none;font-size:.75rem;font-weight:600;
  color:var(--ink-3);padding:.625rem .25rem;min-height:var(--tap);cursor:pointer;position:relative}
.o-mail-Chatter_topbar button[aria-selected=true]{color:var(--ink);background:var(--surface)}
.o-mail-Chatter_topbar button[aria-selected=true]::after{content:'';position:absolute;left:.75rem;right:.75rem;
  bottom:-1px;height:2px;background:var(--ink);border-radius:2px}
.o-mail-Composer_body{padding:.6875rem .75rem}
.o-mail-Composer_recipients{font-size:.71875rem;color:var(--ink-3);margin-bottom:.5rem;display:flex;
  flex-wrap:wrap;gap:.3125rem;align-items:center}
.o-mail-Recipient{--bdg-font:.6875rem;--bdg-weight:600;--bdg-pad:1px .375rem}
/* This is the one that was reported: tapping into it zoomed the layout viewport,
   and a home-screen app has no chrome to pinch the zoom back out with, so the
   page stayed too wide until the app was relaunched. 16px is the size iOS wants;
   the 1.25 line keeps the box drawing 20px lines, so the fixed 76px still shows
   as much of a draft as it did before. */
.o-mail-Composer textarea{width:100%;border:1px solid var(--line);border-radius:.625rem;
  background:var(--surface-2);padding:.5625rem .625rem;font-size:16px;line-height:1.25;
  color:var(--ink);resize:none;outline:none;
  min-height:4.75rem;transition:border-color .15s,background .15s}
.o-mail-Composer textarea:focus{border-color:var(--accent);background:var(--surface)}
.o-mail-Composer.note textarea{background:var(--note-bg);border-color:var(--note-line)}
.o-mail-Composer_actions{display:flex;align-items:center;gap:.4375rem;margin-top:.5625rem}
.o-mail-Composer_button{--btn-h:2rem;--btn-radius:.5625rem;--btn-icon:.9375rem;--btn-font:.8125rem;--btn-weight:700}
.o-mail-Composer_spacer{flex:1}
.o-mail-Composer_send{--btn-h:2rem;--btn-pad:0 .9375rem;--btn-font:.8125rem;--btn-radius:.5625rem;flex:0 0 auto}
.o-mail-MentionList{display:none;position:absolute;left:.75rem;right:.75rem;top:calc(100% - 2px);z-index:9;
  background:var(--surface);border:1px solid var(--line-strong);border-radius:.6875rem;
  box-shadow:0 12px 28px -10px rgba(10,14,22,.28);overflow:hidden}
.o-mail-MentionList.on{display:block}
.o-mail-MentionList_item{display:flex;align-items:center;gap:.5625rem;padding:.5625rem .6875rem;cursor:pointer;
  border-bottom:1px solid var(--line);font-size:.8125rem;width:100%;background:none;border-left:0;
  border-right:0;border-top:0;text-align:left}
.o-mail-MentionList_item:last-child{border-bottom:0}
.o-mail-MentionList_item:active{background:var(--tap-bg)}
.o-mail-MentionList_avatar{--av:1.5rem;--av-radius:.4375rem;--av-font:.59375rem;--av-bg:var(--accent-soft);
  --av-fg:var(--accent);--av-border:transparent}
.o-mail-MentionList_role{margin-left:auto;font-size:.6875rem;color:var(--ink-3)}

/* feed */
.o-mail-Thread{padding:2px 0 .375rem}
.o-mail-DateSection,.o-mail-Thread_new{display:flex;align-items:center;gap:.625rem;margin:.875rem 2px .625rem}
.o-mail-DateSection::before,.o-mail-DateSection::after{content:'';flex:1;height:1px;background:var(--line)}
.o-mail-DateSection span{font-size:.65625rem;font-weight:650;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink-3)}
.o-mail-Thread_new::before,.o-mail-Thread_new::after{content:'';flex:1;height:1px;
  background:color-mix(in srgb,var(--accent) 45%,transparent)}
.o-mail-Thread_new span{font-size:.65625rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--accent)}

.o-mail-Message{display:flex;gap:.625rem;margin-bottom:.625rem}
.o-mail-Message_avatar{--av:1.875rem;--av-radius:.5625rem;--av-font:.65625rem;--av-icon:.875rem}
.o-mail-Message_content{flex:1;min-width:0}
.o-mail-Message_header{display:flex;align-items:baseline;gap:.4375rem;margin-bottom:3px}
.o-mail-Message_author{font-size:.8125rem;font-weight:640;letter-spacing:-.01em;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.o-mail-Message_date{font-size:.6875rem;color:var(--ink-3);margin-left:auto;flex:0 0 auto;
  font-variant-numeric:tabular-nums}
.o-mail-Message_type{--bdg-border:var(--line);--bdg-fg:var(--ink-3)}
/* pre-wrap belongs on the TEXT, never on the bubble: on the bubble it also
   renders the template's own newlines and indentation, which showed up as a
   first line pushed inwards and a hole above the attachments. */
.o-mail-Message_bubble{--sf-radius:.6875rem;padding:.625rem .6875rem;font-size:.84375rem;line-height:1.5;
  white-space:normal;word-break:break-word}
.o-mail-Message_text{white-space:pre-wrap}
.o-mail-Mention{background:var(--accent-soft);color:var(--accent);font-weight:640;border-radius:.3125rem;padding:0 3px}

/* 1. comment */
.o-mail-Message.o_comment .o-mail-Message_avatar{--av-bg:var(--accent-soft);--av-fg:var(--accent);--av-border:transparent}
/* 2. mention — accent rail + tint */
.o-mail-Message.o_mention .o-mail-Message_avatar{--av-bg:var(--accent-soft);--av-fg:var(--accent);--av-border:transparent}
.o-mail-Message.o_mention .o-mail-Message_bubble{border-color:color-mix(in srgb,var(--accent) 35%,var(--line));
  background:linear-gradient(90deg,var(--accent-soft),var(--surface) 55%);border-left:3px solid var(--accent)}
.o-mail-Message.o_mention .o-mail-Message_type{--bdg-bg:var(--accent-soft);--bdg-fg:var(--accent);--bdg-border:transparent}
/* 3. internal note — warm, dashed */
.o-mail-Message.o_note .o-mail-Message_avatar{--av-bg:var(--warn-soft);--av-fg:var(--warn);--av-border:var(--warn-line)}
.o-mail-Message.o_note .o-mail-Message_bubble{background:var(--note-bg);border:1px dashed var(--note-line);box-shadow:none}
.o-mail-Message.o_note .o-mail-Message_type{--bdg-bg:var(--warn-soft);--bdg-fg:var(--warn);--bdg-border:transparent}
.o-mail-Message.o_note .lock{font-size:.6875rem;color:var(--warn);margin-top:.4375rem;display:flex;align-items:center;gap:.3125rem}
.o-mail-Message.o_note .lock svg{width:.75rem;height:.75rem;stroke:currentColor;fill:none;stroke-width:1.8}
/* 4. email */
.o-mail-Message.o_email .o-mail-Message_avatar{--av-bg:var(--info-soft);--av-fg:var(--info);--av-border:var(--info-line)}
.o-mail-Message.o_email .o-mail-Message_bubble{padding:0;overflow:hidden;white-space:normal}
.o-mail-Email_header{padding:.5625rem .6875rem;border-bottom:1px solid var(--line);background:var(--surface-2)}
.o-mail-Email_subject{font-size:.8125rem;font-weight:620;letter-spacing:-.01em}
.o-mail-Email_addresses{font-size:.6875rem;color:var(--ink-3);margin-top:2px}
.o-mail-Email_body{padding:.625rem .6875rem;font-size:.8125rem;line-height:1.5;color:var(--ink-2)}
.o-mail-Email_footer{padding:.5rem .6875rem;border-top:1px solid var(--line);display:flex;align-items:center;gap:.4375rem}
.o-mail-Email_status.ok{--bdg-bg:var(--accent-soft);--bdg-fg:var(--accent)}
.o-mail-Email_status.bad{--bdg-bg:var(--danger-soft);--bdg-fg:var(--danger)}
.o-mail-Email_status.wait{--bdg-bg:var(--warn-soft);--bdg-fg:var(--warn)}
.o-mail-Email_footer .why{font-size:.6875rem;color:var(--ink-3)}
/* 5. tracking / field changes — no bubble, ledger style */
.o-mail-Message.o_tracking .o-mail-Message_avatar{--av-bg:var(--surface-2);--av-fg:var(--ink-3);border-style:dashed}
.o-mail-Message_tracking{border-left:2px solid var(--line-strong);padding:2px 0 2px .6875rem;margin-top:2px}
.o-mail-TrackingValue{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;
  gap:1px .875rem;padding:.3125rem 0}
.o-mail-TrackingValue_field{font-size:.6875rem;font-weight:600;letter-spacing:.03em;text-transform:uppercase;
  color:var(--ink-3);flex:0 1 auto;min-width:0}
.o-mail-TrackingValue_values{font-size:.8125rem;letter-spacing:-.01em;font-variant-numeric:tabular-nums;
  flex:0 1 auto;min-width:0;margin-left:auto;text-align:right}
.o-mail-TrackingValue_values .old{color:var(--danger);opacity:.82;text-decoration:line-through;
  text-decoration-color:var(--danger);text-decoration-thickness:1px}
.o-mail-TrackingValue_values .new{color:var(--accent);font-weight:640;white-space:nowrap}
.o-mail-TrackingValue_values .new::before{content:'⟶';display:inline-block;padding:0 .5625rem;color:var(--ink-3);
  font-weight:400;font-size:.78125rem;text-decoration:none}
/* 6. stage change — centred transition */
.o-mail-StageChange{display:flex;align-items:center;justify-content:center;gap:.5rem;margin:.75rem 0;flex-wrap:wrap}
.o-mail-StageChange_pill{font-size:.6875rem;font-weight:650;padding:3px .5625rem;border-radius:.4375rem;
  background:var(--surface-2);border:1px solid var(--line);color:var(--ink-3)}
.o-mail-StageChange_pill.to{background:var(--ink);border-color:var(--ink);color:var(--surface)}
.o-mail-StageChange_arrow{color:var(--ink-3);font-size:.75rem}
.o-mail-StageChange_author{font-size:.6875rem;color:var(--ink-3);width:100%;text-align:center}
/* 7-9. activities */
.o-mail-Activity{--sf-radius:.75rem}
.o-mail-Activity_header{display:flex;align-items:center;gap:.5rem;padding:.5625rem .6875rem;
  border-bottom:1px solid var(--line);flex-wrap:wrap}
.o-mail-Activity_kind{font-size:.8125rem;font-weight:620;letter-spacing:-.01em;flex:1 1 60%;min-width:0}
.o-mail-Activity_body{padding:.5625rem .6875rem;font-size:.8125rem;line-height:1.45}
.o-mail-Activity_meta{font-size:.71875rem;color:var(--ink-3);margin-top:.3125rem}
.o-mail-Activity_footer{display:flex;gap:.4375rem;padding:.5625rem .6875rem;border-top:1px solid var(--line);
  background:var(--surface-2)}
.o-mail-Activity_footer .o_btn{--btn-h:2rem;--btn-radius:.5625rem;--btn-font:.78125rem;--btn-weight:600;
  --btn-bg:var(--surface);--btn-fg:var(--ink);flex:1}
.o-mail-Activity_footer .o_btn.go{--btn-bg:var(--accent);--btn-border:var(--accent);--btn-fg:var(--accent-ink)}
.o-mail-Activity_due{--bdg-font:.65625rem}
.o-mail-Message.o_activity_planned .o-mail-Message_avatar{--av-bg:var(--surface-2);--av-fg:var(--ink-2)}
.o-mail-Message.o_activity_planned .o-mail-Activity_due{--bdg-bg:var(--surface-2);--bdg-fg:var(--ink-2);--bdg-border:var(--line)}
.o-mail-Message.o_activity_today .o-mail-Message_avatar{--av-bg:var(--warn-soft);--av-fg:var(--warn);--av-border:var(--warn-line)}
.o-mail-Message.o_activity_today .o-mail-Activity_due{--bdg-bg:var(--warn);--bdg-fg:#fff}
.o-mail-Message.o_activity_overdue .o-mail-Message_avatar{--av-bg:var(--danger-soft);--av-fg:var(--danger);--av-border:transparent}
.o-mail-Message.o_activity_overdue .o-mail-Activity{border-color:color-mix(in srgb,var(--danger) 35%,var(--line))}
.o-mail-Message.o_activity_overdue .o-mail-Activity_header{background:var(--danger-soft)}
.o-mail-Message.o_activity_overdue .o-mail-Activity_due{--bdg-bg:var(--danger);--bdg-fg:#fff}
.o-mail-Message.o_activity_done .o-mail-Message_avatar{--av-bg:var(--accent-soft);--av-fg:var(--accent);--av-border:transparent}
.o-mail-Message.o_activity_done .o-mail-Activity{opacity:.88}
.o-mail-Message.o_activity_done .o-mail-Activity_kind{text-decoration:line-through;
  text-decoration-color:var(--line-strong);color:var(--ink-2)}
.o-mail-Message.o_activity_done .o-mail-Activity_due{--bdg-bg:var(--accent-soft);--bdg-fg:var(--accent)}
.o-mail-Activity_feedback{margin-top:.4375rem;padding:.4375rem .5625rem;border-left:2px solid var(--accent);
  background:var(--accent-soft);border-radius:0 .5rem .5rem 0;font-size:.78125rem;color:var(--ink-2)}
/* 10. attachments */
.o-mail-Message.o_attachment .o-mail-Message_avatar{--av-bg:var(--surface-2);--av-fg:var(--ink-2)}
.o-mail-AttachmentList{display:flex;flex-direction:column;gap:.4375rem;margin-top:.5rem}
.o-mail-Attachment{--sf-bg:var(--surface-2);--sf-radius:.625rem;--sf-shadow:none;background:var(--surface-2);
  border:1px solid var(--line);border-radius:.625rem;display:flex;align-items:center;gap:.5625rem;
  padding:.5rem .625rem;cursor:pointer;width:100%;text-align:left}
.o-mail-Attachment_type{width:1.75rem;height:1.75rem;flex:0 0 1.75rem;border-radius:.4375rem;display:grid;place-items:center;
  font-size:.5625rem;font-weight:700;color:#fff;background:var(--ink-3)}
.o-mail-Attachment_type.pdf{background:#b04a4a}.o-mail-Attachment_type.xls{background:#1f6f4a}
.o-mail-Attachment_type.img{background:#31507f}.o-mail-Attachment_type.doc{background:#5a6170}
.o-mail-Attachment_name{font-size:.78125rem;font-weight:560;letter-spacing:-.01em;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;flex:1}
.o-mail-Attachment_size{font-size:.6875rem;color:var(--ink-3);flex:0 0 auto}
/* 12. system / automated + follower events — quiet, no bubble */
.o-mail-Notification{display:flex;align-items:center;gap:.5625rem;padding:.4375rem 0;color:var(--ink-3);font-size:.75rem}
.o-mail-Notification .o_avatar{--av:1.375rem;--av-radius:.4375rem;--av-icon:.75rem;--av-fg:var(--ink-3)}
.o-mail-Notification b{color:var(--ink-2);font-weight:600}
.o-mail-Notification .o_notif_date{margin-left:auto;font-size:.6875rem;flex:none}

.o-mail-ActivityForm{padding:.6875rem .75rem;border-top:1px solid var(--line);display:flex;
  flex-direction:column;gap:.5625rem}
/* The same 16px, and here it is free: 9px of padding either side of a 20px line
   plus the border is 40px, which is the min-height these rows already carried, so
   the type picker, the summary and the date all keep the heights they had. (A
   select sizes its own text and ignores the line-height; the min-height is what
   holds it, and it measures the same 40px either way.) */
.o-mail-ActivityForm input,.o-mail-ActivityForm select{width:100%;border:1px solid var(--line);
  border-radius:.5625rem;padding:.5625rem .6875rem;background:var(--surface-2);min-height:2.5rem;
  font-size:16px;line-height:1.25}
.o-mail-ActivityForm .cr{display:flex;gap:.5rem;align-items:center}

/* ═══ 7. STATES, LOGIN, BANNERS ═════════════════════════════════════════════ */
.o_nocontent_helper{display:flex;flex-direction:column;align-items:center;text-align:center;
  padding:3.625rem 2.5rem;gap:.375rem}
.o_nocontent_icon{width:2.875rem;height:2.875rem;border-radius:.8125rem;border:1px solid var(--line);
  background:var(--surface);display:grid;place-items:center;margin-bottom:.375rem}
.o_nocontent_icon svg{width:1.25rem;height:1.25rem;stroke:var(--ink-3);fill:none;stroke-width:1.6;stroke-linecap:round}
.o_nocontent_helper b{font-size:.9375rem;font-weight:620;letter-spacing:-.01em;color:var(--ink)}
.o_nocontent_helper span{font-size:.8125rem;color:var(--ink-2);max-width:26ch}
.o_nocontent_helper.sm{padding:1.875rem 1.25rem}
.o_list_footer{padding:1rem;text-align:center;font-size:.71875rem;color:var(--ink-3)}

.skel{height:4.125rem;border-radius:var(--r);background:var(--surface);border:1px solid var(--line);
  margin:0 .75rem .5rem;opacity:.6;animation:p 1.3s ease-in-out infinite}
@keyframes p{50%{opacity:.28}}
.banner{margin:.75rem;padding:.6875rem .8125rem;border-radius:.625rem;font-size:.84375rem;border:1px solid var(--line);
  background:var(--surface);color:var(--ink-2)}
.banner.err{border-color:var(--danger);color:var(--danger)}
.note{padding:0 .25rem .75rem;font-size:.75rem;color:var(--ink-3)}

.login{max-width:23.75rem;margin:0 auto;padding:3.5rem 1.375rem}
.login h1{font-size:1.4375rem;letter-spacing:-.02em;margin:0 0 .25rem}
.login p{color:var(--ink-2);margin:0 0 1.625rem;font-size:.875rem}
.login label{display:block;font-size:.78125rem;color:var(--ink-2);margin:.875rem 0 .3125rem}
.login input{width:100%;border:1px solid var(--line);border-radius:.625rem;padding:.6875rem .75rem;
  background:var(--surface);outline:none}
.login input:focus{border-color:var(--accent)}
.login .o_btn{margin-top:1.25rem;width:100%;--btn-h:2.875rem;--btn-radius:.75rem;--btn-font:.9375rem}

/* The iOS install hint and the notifications banner.

   These are IN THE FLEX COLUMN, between the scroller and the bar, and that is a
   fix rather than a style. As fixed overlays they covered the bottom of every
   screen: the last field of a form could not be scrolled clear of them, the
   record action bar sat underneath one, and the new-record button was not merely
   overlapped but unclickable — the banner swallowed the tap. In the column they
   push content up instead, and nothing can hide behind them.

   They are not part of any screen, so render() re-mounts them after it redraws. */
.install-hint,.push-banner{flex:none;display:flex;align-items:center;gap:.625rem;
  margin:0 .75rem .625rem;padding:.75rem .75rem .75rem .875rem;border-radius:.75rem;
  background:var(--surface);border:1px solid var(--line-strong);box-shadow:0 4px 18px rgba(0,0,0,.10)}
.install-hint div,.push-banner div{flex:1;font-size:.8125rem;color:var(--ink-2);line-height:1.5}
.install-hint .h,.push-banner .h{display:block;color:var(--ink);font-weight:620;font-size:.84375rem;margin-bottom:2px}
.install-hint b,.push-banner b{color:var(--ink);font-weight:620}
.install-hint .shr{display:inline-block;border:1px solid var(--line-strong);border-radius:.3125rem;
  padding:0 .3125rem;font-size:.75rem;color:var(--ink)}
.install-hint button{flex:none;width:2rem;height:2rem;border:0;background:none;font-size:1.25rem;
  color:var(--ink-3);cursor:pointer;line-height:1}
.push-banner button{flex:none;min-height:2.125rem;padding:0 .875rem;border-radius:.5625rem;font-size:.8125rem;
  font-weight:600;border:1px solid var(--accent);background:var(--accent);color:var(--accent-ink);cursor:pointer}
.push-banner button:disabled{opacity:.45}

/* A phone-shaped frame on anything wider than a phone. The app is built for a
   handset; a 1400px-wide list row is not a desktop layout, it is a stretched one. */
@media (min-width:560px){
  body{background:#e9ebef}
  @media (prefers-color-scheme:dark){ body{background:#08090a} }
  .app{left:50%;transform:translateX(-50%);width:100%;max-width:430px;
    border-left:1px solid var(--line-strong);border-right:1px solid var(--line-strong)}
}
