/* web-account-utils.css - PLAN 005. Login, trial, Settings, Profile, Resources and the
   membership / confirm / get-app cards, sized for a browser.

   ADDITIVE OVERRIDE LAYER. member.html and widget.html are owned by other agents right now and are
   never edited by this file. Everything here beats an existing declaration with the scoped selector's
   own specificity, or - in the four places noted below - with an !important that is beating an INLINE
   style attribute, which is the only thing a stylesheet cannot outrank any other way.

   EVERY RULE IN THIS FILE IS SCOPED UNDER :root[data-ui-surface="web-app"]. There are no exceptions,
   not even a sentinel: web-surface.css already owns the only three deliberately unscoped rules in the
   web layer. member.html, widget.html, the marketing shell, the floating launcher and the in-course
   embeds all load these same bytes, and only the full browser /app shell (and the widget it frames)
   ever carries the marker. An unscoped rule here would ship into the packaged Android and iOS apps.

   LOAD ORDER: after web-surface.css, which defines the tokens and the sentinel. Both <link>s must
   precede <script src="/web-surface.js"> in both documents (web-surface.css explains why).

   SPECIFICITY NOTE. member.html's own <style> blocks sit far down the document, so they come AFTER
   this file in source order. A tie therefore loses. Every selector below is written to WIN on
   specificity, never on order: `:root[data-ui-surface="web-app"]` alone contributes (0,1,1), so a
   scoped rule outranks the equivalent unscoped one before its own selector is even counted. Where the
   existing rule is itself compound (`body.signin-only #loginModal`, `.ov.res-sheet #ovCard`) the scoped
   selector repeats that compound so it still wins.

   VALUES ARE WEB-DESIGN-CONTRACT.md + plan 005's geometry table. Nothing here changes a brand colour,
   an asset, a price, a label, a link destination, an entitlement, a field option or a calculation. */


/* ==================================================================================================
   TOKENS
   ================================================================================================== */
:root[data-ui-surface="web-app"]{
  /* Widths (plan 005 "Concrete target geometry") */
  --web-auth-max:420px;                              /* Login / trial / membership / confirm / get-app */
  --web-utility-w:600px;                             /* Settings + Profile card                        */
  --web-resources-w:min(760px,calc(100vw - 48px));   /* Resources sheet                                */

  /* Height caps. Held as whole min() expressions so the narrow block can swap one token instead of
     restating four rules. `height:var(--web-utility-h)` parses exactly as if the min() were inline. */
  --web-utility-h:min(720px,calc(100dvh - 48px));
  --web-resources-h:min(680px,calc(100dvh - 48px));
  --web-utility-header:64px;

  /* Spacing */
  --web-dialog-padding:24px;   /* auth-family cards: the overlay gutter AND the card's own padding   */
  --web-sheet-gutter:24px;     /* utility overlays (.set, .pf, .ov.res-sheet) outer gutter           */

  /* Type roles */
  --web-label-size:14px;    --web-label-line:20px;
  --web-caption-size:12.5px; --web-caption-line:18px;
  --web-field-size:16px;    --web-field-line:24px;

  /* Control floors */
  --web-row-min:52px;         /* Settings + Resources list rows                                     */
  --web-option-row-min:64px;  /* membership option rows                                             */
  --web-tap:44px;             /* existing tap floor, restated so overrides cannot drop below it      */
  --web-cta:48px;             /* login CTA / login fields                                           */
  --web-auth-field:52px;      /* trial signup fields and its CTA                                    */
  --web-avatar-lg:80px;       /* profile avatar                                                     */
}

/* Narrow browser column. Below 600px the cards stop being cards and become the screen, minus a
   gutter. The auth family keeps a 16px gutter (viewport - 32); the utility sheets keep 12px
   (viewport - 24), which is plan 005's split, not one number applied twice. */
@media (max-width:599px){
  :root[data-ui-surface="web-app"]{
    --web-auth-max:calc(100vw - 32px);
    --web-utility-w:calc(100vw - 24px);
    --web-resources-w:calc(100vw - 24px);
    --web-utility-h:calc(100dvh - 24px);
    --web-resources-h:calc(100dvh - 24px);
    --web-dialog-padding:16px;
    --web-sheet-gutter:12px;
  }
}


/* ==================================================================================================
   LOGIN  (member.html #loginModal / .lg-card - both the full-page signin-only role and the in-app modal)
   ==================================================================================================
   THE FOUR !importants IN THIS SECTION ARE BEATING INLINE STYLE ATTRIBUTES, NOT OTHER RULES.
   #loginModal carries style="...padding:18px;align-items:center;justify-content:center..." on the
   element (member.html markup), and .lg-card carries style="max-width:380px;...padding:22px 20px".
   An inline declaration outranks every selector, so these three properties cannot be reached from a
   stylesheet at all without !important. Nothing else in this file uses one.

   WHAT IS DELIBERATELY NOT TOUCHED: align-items / justify-content. body.signin-only #loginModal pins
   justify-content:flex-start and relies on `margin:auto` on the card to do the centring, because
   centring a flex item taller than its container overflows BOTH ends and put the logo above the
   viewport on a 1280x720 laptop (member.html records the measurement: brand top -47px, already at
   scrollTop 0). Plan 005 repeats the warning. This file changes padding and width only. */
:root[data-ui-surface="web-app"] #loginModal{
  padding:var(--web-dialog-padding) !important;   /* inline padding:18px */
  box-sizing:border-box;
}
/* The full-page role keeps its safe-area top and its generous bottom. The bottom stays at 40px + --sab
   rather than dropping to 24: that number is the measured clearance that stopped the card being cut by
   the Android gesture bar, and a browser costs nothing by keeping it. Sides and top move to 24. */
:root[data-ui-surface="web-app"] body.signin-only #loginModal{
  padding:calc(var(--web-dialog-padding) + var(--sat)) var(--web-dialog-padding)
          calc(40px + var(--sab)) !important;
}
:root[data-ui-surface="web-app"] #loginModal .lg-card{
  max-width:var(--web-auth-max) !important;       /* inline max-width:380px */
  padding:var(--web-dialog-padding) !important;   /* inline padding:22px 20px */
  box-sizing:border-box;
}

/* Fields: 48px tall, 24px leading. The FONT SIZE IS LEFT ALONE ON PURPOSE - both fields already carry
   an inline font-size:var(--fs-title), which is 17px, and the contract asks for 16px as a MINIMUM
   actual input size. Forcing them to 16 would need an !important to make the text smaller than it is
   today, which is the wrong direction and buys nothing. */
:root[data-ui-surface="web-app"] #loginEmail,
:root[data-ui-surface="web-app"] #loginCode{
  min-height:var(--web-cta);
  line-height:var(--web-field-line);
  box-sizing:border-box;
}
/* CTA 48px. #loginSend already declares it inline; #loginVerify never did - it is padding:12px around
   a 15px label, which renders at 41px, under the 44px tap floor. */
:root[data-ui-surface="web-app"] #loginSend,
:root[data-ui-surface="web-app"] #loginVerify,
:root[data-ui-surface="web-app"] #loginBioBtn{
  min-height:var(--web-cta);
  box-sizing:border-box;
}
/* Exit 44px. body.signin-only already gives this button a 44px pill; in the in-app MODAL role it is a
   bare <button> with a chevron and the word Back, and it renders well under the floor. */
:root[data-ui-surface="web-app"] #loginClose{
  min-height:var(--web-tap);
  min-width:var(--web-tap);
  display:inline-flex;
  align-items:center;
  justify-content:center;
  gap:var(--web-space-1,4px);
  box-sizing:border-box;
}
/* Legal 12.5/18. The size is already --fs-fine (12.5px); only the leading moves, 1.6 -> 18px. */
:root[data-ui-surface="web-app"] .lg-legal{
  font-size:var(--web-caption-size);
  line-height:var(--web-caption-line);
}


/* ==================================================================================================
   TRIAL SIGN-UP  (widget.html .trial-signup-card)
   ==================================================================================================
   The card is styled where it stands. hovisTrialFloat, the overlay's connection to the shell's real
   background screen, the iframe, the form markup and every focus/resize handler are untouched - this
   section sets width, padding and control size and nothing else. */
:root[data-ui-surface="web-app"] .trial-signup-card{
  max-width:var(--web-auth-max);
  margin-left:auto;
  margin-right:auto;
  padding:var(--web-dialog-padding);
  box-sizing:border-box;
}
/* 52px controls, and a 16px FLOOR on the field text. max() can only raise it: today these fields
   inherit .stratform's --fs-ui (14.5px), which is below the contract's 16px minimum actual input size
   and is also the size that makes iOS Safari zoom the page on focus. No field ever gets smaller. */
:root[data-ui-surface="web-app"] .trial-signup-card input,
:root[data-ui-surface="web-app"] .trial-signup-card select{
  min-height:var(--web-auth-field);
  font-size:max(var(--web-field-size),var(--fs-ui));
  line-height:var(--web-field-line);
  box-sizing:border-box;
}
:root[data-ui-surface="web-app"] .trial-signup-card .stratgo{
  min-height:var(--web-auth-field);
  box-sizing:border-box;
}
:root[data-ui-surface="web-app"] #trial-signup-legal{
  font-size:var(--web-caption-size);
  line-height:var(--web-caption-line);
}


/* ==================================================================================================
   SETTINGS  (member.html .set / .set-shell / .set-h / .set-b / .set-row)
   ==================================================================================================
   620 -> 600 wide, and the height cap becomes an explicit browser cap rather than a percentage of the
   scrim. The local scroll region stays exactly where it is: .set-b still owns the scrolling, so the
   page behind never moves.

   THE 52px ROW IS WEB-ONLY AND THAT IS THE WHOLE POINT. member.html's .set-row comment records a
   deliberate decision to hold min-height at 46px on the handset - Aye's vc118 note was that the sheet
   is too LONG - and this rule cannot reach it. Under the marker the sheet is a 600px card on a desktop
   with its own scroller, where the contract asks for 52px rows; on Android and iOS the row is still 46. */
:root[data-ui-surface="web-app"] .set{
  padding:var(--web-sheet-gutter);
  box-sizing:border-box;
}
:root[data-ui-surface="web-app"] .set-shell{
  max-width:var(--web-utility-w);
  height:var(--web-utility-h);
  border-radius:var(--r-card);
  flex:none;
}
/* 72px -> 64px. The header was 14px of padding either side of a 44px close button; 10px either side
   lands exactly on the 64px the contract asks for without moving the button off the tap floor. */
:root[data-ui-surface="web-app"] .set-h{
  min-height:var(--web-utility-header);
  padding:calc(10px + var(--sat)) 16px 10px;
  box-sizing:border-box;
}
/* The scroller keeps its measured bottom clearance (40px + --sab). Only the inline padding is a
   contract value, and it is already 16 on the wide side; the narrow block below takes it to 12. */
:root[data-ui-surface="web-app"] .set-b{
  padding:16px 16px calc(40px + var(--sab));
}
:root[data-ui-surface="web-app"] .set-row{
  min-height:var(--web-row-min);
  padding:8px 12px;
}
:root[data-ui-surface="web-app"] .set-row .lb{
  font-size:var(--web-label-size);
  line-height:var(--web-label-line);
}
:root[data-ui-surface="web-app"] .set-row .sub{
  font-size:var(--web-caption-size);
  line-height:var(--web-caption-line);
}
/* Controls keep their 44px floor. Restated rather than assumed, because .set-row's padding just moved
   and a floor that is only implied by padding is not a floor. */
:root[data-ui-surface="web-app"] .set-row .nickin,
:root[data-ui-surface="web-app"] .set-row .seg button{
  min-height:var(--web-tap);
}


/* ==================================================================================================
   PROFILE  (member.html .pf / .pf-h / .pf-b)
   ==================================================================================================
   PLAN 005 ASKS FOR A .web-profile-card WRAPPER MOVED IN BY SCRIPT. THIS FILE DOES NOT BUILD ONE, AND
   THE REASON IS STRUCTURAL, NOT PREFERENCE: a wrapper needs JavaScript, JavaScript needs a <script>
   tag, and member.html - the only place that tag could go - is locked to another agent for this whole
   session. A JS file nothing loads is not a deliverable. So the card is built out of the two elements
   that are already there.

   .pf-h and .pf-b are adjacent siblings in a column flexbox. Given the same max-width, the same
   background, a shared 1px border and complementary corner radii they ARE one card - .pf-h's existing
   border-bottom becomes the card's internal divider. No node moves, no handler is rebound, the overlay
   id, the form id, every input id and the save/discard logic are untouched by construction.

   .pf then becomes the scrim, which is the role plan 005 assigns it. rgba(0,0,0,.55) is not a new
   colour: it is the exact scrim .set and .ov already paint. The slide-up transform and the visibility
   handling are left alone - motion belongs to plan 007. */
/* THE CONTAINMENT HAS TO COME OFF FIRST, AND THIS IS A MEASURED FINDING, NOT A TIDY-UP.
   #pfSheet is a child of .set-b (member.html markup - the Profile sheet lives inside the Settings
   scroller). .set-b picked up `contain:paint` + `transform:translateZ(0)` on 2026-09-07 to stop the
   Settings list feeling laggy on Aye's handset. Both of those make .set-b the CONTAINING BLOCK for a
   position:fixed descendant, and contain:paint additionally clips it. Measured at 1280x720 on today's
   build: .pf-h renders 618px wide, not 1280 - the "position:fixed;inset:0" Profile sheet is not covering
   the viewport at all, it is confined to the inside of the Settings card, and its own CSS comment says
   it is meant to sit ABOVE Settings at z-index 75.
   Under the marker only, the containment is released so the sheet is the dialog its code describes.
   NATIVE KEEPS contain:paint AND translateZ VERBATIM - the scroll fix was for a phone webview blurring
   five surfaces, this rule cannot reach it, and a desktop browser does not need it. will-change is left
   in place; only the two properties that create the containing block are cleared. */
:root[data-ui-surface="web-app"] .set-b{
  contain:none;
  transform:none;
}
:root[data-ui-surface="web-app"] .pf{
  background:rgba(0,0,0,.55);
  align-items:center;
  justify-content:center;
  padding:var(--web-sheet-gutter);
  box-sizing:border-box;
}
:root[data-ui-surface="web-app"] .pf-h,
:root[data-ui-surface="web-app"] .pf-b{
  width:100%;
  max-width:var(--web-utility-w);
  background:var(--bg);
  border-left:1px solid var(--line);
  border-right:1px solid var(--line);
  box-sizing:border-box;
}
:root[data-ui-surface="web-app"] .pf-h{
  min-height:var(--web-utility-header);
  padding:calc(10px + var(--sat)) 12px 10px;
  border-top:1px solid var(--line);
  border-radius:var(--r-card) var(--r-card) 0 0;
}
/* flex:0 1 auto, not the inherited flex:1 - the body must stop at its content or at the card cap,
   or it stretches to the full scrim height and there is no card. */
:root[data-ui-surface="web-app"] .pf-b{
  flex:0 1 auto;
  max-height:calc(var(--web-utility-h) - var(--web-utility-header));
  padding:16px 16px calc(28px + var(--sab));
  border-bottom:1px solid var(--line);
  border-radius:0 0 var(--r-card) var(--r-card);
}
/* 96 -> 80. The avatar is the same element, same image, same object-fit; only the circle is smaller. */
:root[data-ui-surface="web-app"] .av-xl{
  width:var(--web-avatar-lg);
  height:var(--web-avatar-lg);
}
/* Field 16/24 at a 44px floor. Same max() floor as the trial card: --fs-ui is 14.5px today. */
:root[data-ui-surface="web-app"] .pf-b .nickin{
  font-size:max(var(--web-field-size),var(--fs-ui));
  line-height:var(--web-field-line);
  min-height:var(--web-tap);
}
:root[data-ui-surface="web-app"] .pf-btn,
:root[data-ui-surface="web-app"] .pf-back,
:root[data-ui-surface="web-app"] .pf-save{
  min-height:var(--web-tap);
}


/* ==================================================================================================
   RESOURCES  (member.html .ov.res-sheet / #ovCard)
   ==================================================================================================
   The 760px cap is RETAINED, not introduced - the sheet already had it above 900px. What changes is
   that the browser gets one centred card at every width instead of a bottom sheet below 900px, and the
   height cap becomes 100dvh - 48 rather than 100dvh - 40 / 100dvh - --sat.

   .res-sheet is the scope boundary the file already uses: the shared .ov card, which saved deals and
   every other caller mount into, is not selected here and does not move. */
:root[data-ui-surface="web-app"] .ov.res-sheet{
  align-items:center;
  justify-content:center;
  padding:var(--web-sheet-gutter);
  box-sizing:border-box;
}
:root[data-ui-surface="web-app"] .ov.res-sheet #ovCard{
  width:var(--web-resources-w);
  height:var(--web-resources-h);
  max-width:none;
  max-height:none;
  border-radius:var(--r-card);
  flex:none;
}
/* Rows are already --row-comfort (52px) here; restated against the token this plan owns so the floor
   survives a change to --row-comfort made for a phone. Title 14/20. Wrapping is untouched: .res-l keeps
   white-space:normal, so a long title takes a second line and grows the row rather than scrolling. */
:root[data-ui-surface="web-app"] .ov.res-sheet .res-row{
  min-height:var(--web-row-min);
}
:root[data-ui-surface="web-app"] .ov.res-sheet .res-l{
  font-size:var(--web-label-size);
  line-height:var(--web-label-line);
}
/* Search text gets the 16px floor - `1em` here is the inherited size, so max() can only raise it. */
:root[data-ui-surface="web-app"] .ov.res-sheet .res-search input{
  font-size:max(var(--web-field-size),1em);
  line-height:var(--web-field-line);
}
/* 44px floors on search, clear, filter and the Open/Save actions. All four already declare --tap-min;
   restated against this plan's token for the same reason as the row above. */
:root[data-ui-surface="web-app"] .ov.res-sheet .res-search,
:root[data-ui-surface="web-app"] .ov.res-sheet .res-search input,
:root[data-ui-surface="web-app"] .ov.res-sheet .res-search-clear,
:root[data-ui-surface="web-app"] .ov.res-sheet .res-filter-btn,
:root[data-ui-surface="web-app"] .ov.res-sheet .res-filter-clear,
:root[data-ui-surface="web-app"] .ov.res-sheet .res-filter-option,
:root[data-ui-surface="web-app"] .ov.res-sheet .res-a{
  min-height:var(--web-tap);
}


/* ==================================================================================================
   MEMBERSHIP / CONFIRM / GET APP  (.imodal, #buyPop / .buy-pop-ov, .ga-sheet, .ga-ios, .setsheet)
   ==================================================================================================
   Geometry only. Option order, labels, prices, eligibility, buttons, destinations and every badge or
   icon asset are exactly what they were - none of them is selected by anything in this section. */
:root[data-ui-surface="web-app"] .imodal,
:root[data-ui-surface="web-app"] #buyPop,
:root[data-ui-surface="web-app"] .buy-pop-ov,
:root[data-ui-surface="web-app"] .ga-sheet,
:root[data-ui-surface="web-app"] .ga-ios,
:root[data-ui-surface="web-app"] .setsheet{
  padding:var(--web-dialog-padding);
  box-sizing:border-box;
}
:root[data-ui-surface="web-app"] .imodal-card,
:root[data-ui-surface="web-app"] .buy-pop-card,
:root[data-ui-surface="web-app"] .ga-card{
  max-width:var(--web-auth-max);
  padding:var(--web-dialog-padding);
  box-sizing:border-box;
}
:root[data-ui-surface="web-app"] .setsheet > *{
  max-width:var(--web-auth-max);
}
/* Option rows 60 -> 64. */
:root[data-ui-surface="web-app"] .buy-plan{
  min-height:var(--web-option-row-min);
}
:root[data-ui-surface="web-app"] .buy-plan-name,
:root[data-ui-surface="web-app"] .buy-plan-price{
  font-size:var(--web-label-size);
  line-height:var(--web-label-line);
}
:root[data-ui-surface="web-app"] .buy-legal,
:root[data-ui-surface="web-app"] .ga-note,
:root[data-ui-surface="web-app"] .ga-sub{
  font-size:var(--web-caption-size);
  line-height:var(--web-caption-line);
}
/* 44px floors on the dialog actions that do not already declare one.
   B1 (2026-09-08): ".imodal-go" alone was a FLOOR THAT ACTED AS A CEILING on the one control that
   can least afford it. member.html:6939 builds the Buy Lifetime purchase button as
   class="imodal-go buy-plan-go", and member.html:549 gives .buy-plan-go min-height:52px at
   specificity 0,1,0. This selector is 0,3,0, so it won and dragged the purchase CTA down to 44px on
   web only. Checked all five classes rather than just the one that was caught: .imodal-no and
   .ga-close already declare 44px (identical, no effect), .imodal-go / .ga-b / .buy-restore declare
   none (the rule does real work), and .buy-plan-go is the sole case where 44 replaces something
   larger. So it is excluded by name - which is precisely what this comment already claimed the rule
   did. CSS has no "only if lower than what is there", so the exception has to be explicit; anything
   added here later that declares its own floor above 44 needs the same treatment. */
:root[data-ui-surface="web-app"] .imodal-go:not(.buy-plan-go),
:root[data-ui-surface="web-app"] .imodal-no,
:root[data-ui-surface="web-app"] .ga-b,
:root[data-ui-surface="web-app"] .ga-close,
:root[data-ui-surface="web-app"] .buy-restore{
  min-height:var(--web-tap);
  box-sizing:border-box;
}


/* ==================================================================================================
   NARROW BROWSER - the two rules the token swap at the top of the file cannot express
   ==================================================================================================
   Everything else narrows by token. These two scrollers carry a measured bottom clearance that must
   survive, so their padding is written out rather than tokenised, and only the inline half moves. */
@media (max-width:599px){
  :root[data-ui-surface="web-app"] .set-b{
    padding:16px 12px calc(40px + var(--sab));
  }
  :root[data-ui-surface="web-app"] .pf-b{
    padding:16px 12px calc(28px + var(--sab));
  }
}
