.heroUtilityActions{
  position:absolute;
  top:16px;
  left:16px;
  z-index:14;
  display:flex;
  align-items:center;
  gap:8px;
}
.heroUtilityActions .heroSettingsButton{
  position:static!important;
  top:auto!important;
  left:auto!important;
}
.heroUtilityButton{
  position:relative;
  min-height:42px;
  min-width:42px;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  gap:0;
  padding:9px;
  border:1px solid var(--edge-strong,var(--line));
  border-radius:12px;
  background:var(--surface-raised,var(--panel2));
  color:var(--text);
  font:inherit;
  font-size:.78rem;
  font-weight:850;
  cursor:pointer;
  box-shadow:var(--elevation-1,0 8px 20px rgba(0,0,0,.14));
  transition:transform .16s ease,border-color .16s ease,background .16s ease,box-shadow .16s ease;
}
.heroUtilityButton:hover{transform:translateY(-1px);border-color:var(--accent);background:var(--surface-sub,var(--panel2));box-shadow:var(--elevation-2,0 12px 28px rgba(0,0,0,.18))}
.heroUtilityButton:focus-visible{outline:3px solid color-mix(in srgb,var(--accent) 34%,transparent);outline-offset:3px}
.heroUtilityIcon{width:20px;height:20px;display:block;background:var(--accent2);-webkit-mask-position:center;-webkit-mask-repeat:no-repeat;-webkit-mask-size:contain;mask-position:center;mask-repeat:no-repeat;mask-size:contain}
.notificationBellIcon{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M18 9a6 6 0 0 0-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9ZM9.7 21h4.6a2.5 2.5 0 0 1-4.6 0Z' fill='black'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M18 9a6 6 0 0 0-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9ZM9.7 21h4.6a2.5 2.5 0 0 1-4.6 0Z' fill='black'/%3E%3C/svg%3E")}
.accountShortcutIcon{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 12a5 5 0 1 0 0-10 5 5 0 0 0 0 10Zm0 2c-5 0-9 2.7-9 6v2h18v-2c0-3.3-4-6-9-6Z' fill='black'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 12a5 5 0 1 0 0-10 5 5 0 0 0 0 10Zm0 2c-5 0-9 2.7-9 6v2h18v-2c0-3.3-4-6-9-6Z' fill='black'/%3E%3C/svg%3E")}
.notificationBadge{position:absolute;top:2px;right:2px;min-width:18px;height:18px;padding:0 4px;display:grid;place-items:center;border:2px solid var(--panel);border-radius:999px;background:var(--bad,#ff6b6b);color:#fff;font-size:.62rem;font-weight:950;line-height:1;box-shadow:0 4px 12px rgba(0,0,0,.25);z-index:2}
#notificationBellButton{overflow:visible}
.heroUtilityActions{overflow:visible}
body.signed-out .signedInHeaderControl{display:none!important}

.notificationCenterOverlay{position:fixed;inset:0;z-index:10020;display:grid;place-items:center;padding:20px;background:rgba(2,6,12,.68);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}
.notificationCenterOverlay.hidden{display:none!important}
body.notificationCenterOpen{overflow:hidden}
.notificationCenter{width:min(760px,100%);max-height:min(820px,calc(100dvh - 40px));display:flex;flex-direction:column;overflow:hidden;border:1px solid color-mix(in srgb,var(--accent) 48%,var(--line));border-radius:24px;background:linear-gradient(145deg,color-mix(in srgb,var(--panel) 96%,var(--accent) 4%),color-mix(in srgb,var(--panel2) 95%,var(--accent2) 5%));color:var(--text);box-shadow:0 30px 90px rgba(0,0,0,.42),inset 0 1px 0 rgba(255,255,255,.08)}
.notificationCenterHeader{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;padding:20px 22px 17px;border-bottom:1px solid var(--line);background:color-mix(in srgb,var(--panel2) 88%,var(--accent) 12%)}
.notificationCenterEyebrow{display:block;margin-bottom:4px;color:var(--accent2);font-size:.68rem;font-weight:950;letter-spacing:.1em;text-transform:uppercase}
.notificationCenterHeader h2{margin:0;font-size:1.2rem}.notificationCenterHeader p{margin:5px 0 0;color:var(--muted);font-size:.82rem}
.notificationCenterClose{width:40px;height:40px;flex:0 0 40px;border:1px solid var(--line);border-radius:50%;background:var(--surface-input,var(--panel));color:var(--text);font-size:1.5rem;line-height:1;cursor:pointer}
.notificationCenterClose:hover{border-color:var(--accent);color:var(--accent2)}
.notificationCenterToolbar{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:10px 12px;padding:13px 22px;border-bottom:1px solid var(--line)}
.notificationBehaviorNote{margin:0;padding:9px 22px;border-bottom:1px solid var(--line);background:color-mix(in srgb,var(--panel2) 94%,var(--accent) 6%);color:var(--muted);font-size:.7rem;line-height:1.4}
.notificationFilters{display:flex;flex:1 1 auto;gap:7px;overflow-x:auto;padding:2px}.notificationFilter{white-space:nowrap;border:1px solid var(--line);border-radius:999px;padding:7px 11px;background:var(--surface-input,var(--panel));color:var(--muted);font:inherit;font-size:.72rem;font-weight:850;cursor:pointer}.notificationFilter.active{border-color:var(--accent);background:var(--accent-soft);color:var(--text)}
.notificationBulkActions{display:flex;gap:7px}.notificationTextButton{border:0;background:transparent;color:var(--accent2);font:inherit;font-size:.73rem;font-weight:900;cursor:pointer;padding:7px}.notificationTextButton:disabled{opacity:.42;cursor:not-allowed}.notificationTextButton.danger{color:var(--bad)}
.notificationCenterBody{overflow:auto;padding:8px 22px 22px;overscroll-behavior:contain}.notificationGroup{margin-top:15px}.notificationGroupHeading{display:flex;align-items:center;gap:10px;margin:0 0 9px;color:var(--muted);font-size:.7rem;font-weight:950;letter-spacing:.08em;text-transform:uppercase}.notificationGroupHeading::after{content:"";height:1px;flex:1;background:linear-gradient(90deg,var(--line),transparent)}
.notificationList{display:grid;gap:9px}.notificationItem{display:grid;grid-template-columns:42px minmax(0,1fr);gap:8px 12px;align-items:start;padding:12px 13px;border:1px solid var(--line);border-radius:16px;background:var(--surface-raised,var(--panel2));box-shadow:0 8px 22px rgba(0,0,0,.08)}
.notificationItem.unread{border-color:color-mix(in srgb,var(--accent) 55%,var(--line));background:linear-gradient(145deg,color-mix(in srgb,var(--panel2) 90%,var(--accent) 10%),var(--surface-raised,var(--panel2)))}
.notificationItemIcon{width:42px;height:42px;display:grid;place-items:center;border:1px solid color-mix(in srgb,currentColor 38%,var(--line));border-radius:13px;background:color-mix(in srgb,currentColor 11%,transparent);color:var(--accent2)}
.notificationItemIcon::before{content:"";width:21px;height:21px;background:currentColor;-webkit-mask:center/contain no-repeat;mask:center/contain no-repeat;-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M11 17h2v-2h-2v2Zm0-4h2V7h-2v6Zm1-11a10 10 0 1 0 0 20 10 10 0 0 0 0-20Z' fill='black'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M11 17h2v-2h-2v2Zm0-4h2V7h-2v6Zm1-11a10 10 0 1 0 0 20 10 10 0 0 0 0-20Z' fill='black'/%3E%3C/svg%3E")}
.notificationItem.storage .notificationItemIcon{color:var(--accent2)}.notificationItem.inventory .notificationItemIcon{color:var(--warn)}.notificationItem.error .notificationItemIcon{color:var(--bad)}.notificationItem.info .notificationItemIcon{color:var(--good)}
/* Broadcast icons: the image/emoji IS the icon — drop the built-in "i" glyph so it can't show behind a transparent image, and neutralise the accent frame so an image reads as a clean thumbnail. */
.notificationItemIcon.notificationBcastImg::before,.notificationItemIcon.notificationBcastEmoji::before{content:none;display:none}
.notificationItemIcon.notificationBcastImg{border-color:var(--line);background:transparent;color:var(--text)}
.notificationItemCopy{min-width:0}.notificationItemCopy h4{margin:1px 0 4px;font-size:.87rem}.notificationItemCopy p{margin:0;color:var(--muted);font-size:.77rem;line-height:1.45}.notificationBcastMsg strong{color:var(--text);font-weight:850}
/* A blank line in a broadcast arrives as <br><br>. At .77rem/1.45 that is only ~15px and does
   not read as a paragraph break, so a message with sections renders as one wall of text. Give
   the second <br> real space. Deliberately scoped to broadcasts: every other notification body
   is a single sentence and does not want the extra gap. */
.notificationBcastMsg br + br{display:block;content:"";margin-top:.45em}
/* Links in a broadcast body had NO styling, so they fell back to the browser defaults - blue
   unvisited, purple visited. Both are fixed colours chosen for a white page: on the darker themes
   they are low-contrast, and the visited/unvisited difference reads as two unrelated states.
   :visited is pinned to the same colour deliberately - a download link changing appearance after
   one click is noise, not information. The underline carries the affordance so the link is still
   obviously a link on a theme where the accent sits close to the body colour. */
.notificationBcastMsg a,
.notificationBcastMsg a:visited{color:var(--accent);text-decoration:underline;text-underline-offset:2px;text-decoration-thickness:1px;overflow-wrap:anywhere}
.notificationBcastMsg a:hover{color:var(--accent-strong,var(--accent));text-decoration-thickness:2px}
.notificationBcastMsg a:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:2px}
/* Long broadcast messages show a 4-line preview + a "Read more" toggle (clamp removed on expand). */
.notificationMsgClamp{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:4;line-clamp:4;overflow:hidden}
.notificationReadMore{margin:4px 0 0;border:0;background:transparent;color:var(--accent2);font:inherit;font-size:.7rem;font-weight:900;cursor:pointer;padding:2px 0;text-decoration:underline;text-underline-offset:2px}
.notificationReadMore:hover{color:var(--accent)}.notificationItemMeta{display:flex;align-items:center;flex-wrap:wrap;gap:7px;margin-top:7px;color:var(--muted);font-size:.68rem}.notificationItemMeta time{color:var(--text);font-weight:750}.notificationUnreadDot{width:7px;height:7px;border-radius:50%;background:var(--accent);box-shadow:0 0 10px color-mix(in srgb,var(--accent) 70%,transparent)}
.notificationItemActions{grid-column:2;display:flex;flex-direction:row;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:6px;margin-top:1px}.notificationItemAction{min-height:28px;border:0;border-radius:8px;background:transparent;color:var(--accent2);font:inherit;font-size:.67rem;font-weight:900;cursor:pointer;padding:5px 7px}.notificationItemAction:hover{background:color-mix(in srgb,currentColor 10%,transparent)}.notificationItemAction.update{color:var(--good)}.notificationItemAction.delete{color:var(--bad)}
.notificationSnoozeControl{display:flex;align-items:center;gap:3px}.notificationSnoozeControl select{min-width:68px;height:28px;padding:4px 20px 4px 7px;border:1px solid var(--line);border-radius:8px;background:var(--surface-input,var(--panel));color:var(--text);font:inherit;font-size:.65rem;font-weight:750}
.inventoryTable tbody tr.inventoryNotificationTarget{scroll-margin-block:120px;outline:2px solid var(--accent);outline-offset:-2px}.inventoryTable tbody tr.inventoryNotificationTarget td{animation:inventoryNotificationBlink .68s ease-in-out 7;background:color-mix(in srgb,var(--accent) 18%,var(--panel2))}
@keyframes inventoryNotificationBlink{0%,100%{background:color-mix(in srgb,var(--accent) 8%,var(--panel2));box-shadow:inset 0 0 0 0 transparent}50%{background:color-mix(in srgb,var(--accent) 38%,var(--panel2));box-shadow:inset 0 0 0 2px var(--accent)}}
.notificationEmpty{padding:42px 18px;text-align:center;border:1px dashed var(--line);border-radius:18px;color:var(--muted)}.notificationEmptyIcon{width:54px;height:54px;margin:0 auto 12px;border:1px solid color-mix(in srgb,var(--accent) 35%,var(--line));border-radius:50%;display:grid;place-items:center;background:var(--accent-soft);color:var(--accent2);font-size:1.45rem}.notificationEmpty h3{margin:0 0 5px;color:var(--text);font-size:1rem}.notificationEmpty p{margin:0;font-size:.8rem}

.accountStorageProgress{margin:14px 0 4px;padding:14px;border:1px solid color-mix(in srgb,var(--accent) 30%,var(--line));border-radius:15px;background:color-mix(in srgb,var(--panel2) 91%,var(--accent) 9%)}
.accountStorageProgressHeader{display:flex;justify-content:space-between;gap:12px;align-items:center;font-size:.78rem}.accountStorageProgressHeader span{color:var(--muted);font-weight:850}.accountStorageProgressHeader strong{color:var(--text);text-align:right}
.accountStorageTrack{height:10px;margin-top:9px;overflow:hidden;border:1px solid var(--line);border-radius:999px;background:var(--surface-input,var(--panel))}.accountStorageTrack span{display:block;width:0;height:100%;border-radius:inherit;background:linear-gradient(90deg,var(--accent),var(--accent2));transition:width .3s ease}.accountStorageTrack.warning span{background:linear-gradient(90deg,var(--accent),var(--warn))}.accountStorageTrack.danger span{background:linear-gradient(90deg,var(--warn),var(--bad))}.accountStorageProgress p{margin:8px 0 0;color:var(--muted);font-size:.72rem;line-height:1.4}

body.theme-light .notificationCenterOverlay{background:rgba(15,23,42,.42)}
body.theme-light .notificationCenter{box-shadow:0 30px 80px rgba(15,23,42,.24),inset 0 1px 0 #fff}
@media(max-width:760px){.heroUtilityButtonLabel{display:none}.heroUtilityButton{padding:9px;min-width:42px}.heroUtilityActions{gap:6px}}
@media(max-width:620px){.heroUtilityActions{top:12px;left:12px}.heroUtilityActions .heroSettingsButton{width:42px!important;min-width:42px!important;padding:9px!important;gap:0!important}.heroUtilityActions .heroSettingsButton>span:last-child{display:none!important}.notificationCenterOverlay{padding:10px}.notificationCenter{max-height:calc(100dvh - 20px);border-radius:19px}.notificationCenterHeader{padding:17px}.notificationCenterToolbar{align-items:stretch;flex-direction:column;padding:11px 17px}.notificationBehaviorNote{padding:9px 17px}.notificationBulkActions{justify-content:flex-end}.notificationCenterBody{padding:6px 17px 17px}.notificationItem{grid-template-columns:38px minmax(0,1fr);gap:8px 10px;padding:11px}.notificationItemIcon{width:38px;height:38px}.notificationItemActions{gap:3px}.notificationItemAction{padding-inline:5px}.accountStorageProgressHeader{align-items:flex-start;flex-direction:column;gap:3px}}
@media(prefers-reduced-motion:reduce){.heroUtilityButton,.accountStorageTrack span{transition:none}.inventoryTable tbody tr.inventoryNotificationTarget td{animation:none;background:color-mix(in srgb,var(--accent) 28%,var(--panel2))}}
/* The Bambu confirm action changes stock, so it reads as the primary action
   on that notification rather than sitting among the neutral controls. */
.notificationItemAction.confirmDeduct{
  border-color:color-mix(in srgb,var(--good,#4ade80) 60%,var(--line))!important;
  background:color-mix(in srgb,var(--good,#4ade80) 16%,transparent)!important;
  color:var(--text)!important;
  font-weight:800!important;
}
.notificationItemAction.confirmDeduct:hover{
  background:color-mix(in srgb,var(--good,#4ade80) 26%,transparent)!important;
  border-color:var(--good,#4ade80)!important;
}

/* "Print failed" sits beside Confirm & deduct: a stopped print still used
   filament, so it needs an action here too, styled as the cautionary choice. */
.notificationItemAction.printFailed{
  border:1px solid color-mix(in srgb,var(--warn,#d97706) 55%,var(--line))!important;
  background:color-mix(in srgb,var(--warn,#d97706) 14%,transparent)!important;
  color:var(--text)!important;
  font-weight:800!important;
}
.notificationItemAction.printFailed:hover{
  background:color-mix(in srgb,var(--warn,#d97706) 24%,transparent)!important;
  border-color:var(--warn,#d97706)!important;
}

/* A print whose filament has already come off inventory shows a STATUS in the action
   row instead of the two deduct buttons -- both of those would take the same filament
   a second time. It borrows .notificationItemAction for size and rhythm, so it has to
   undo that rule's button affordances: the base sets border:0, cursor:pointer and a
   :hover wash. (The base border:0 is also why .confirmDeduct above, which overrides
   only border-color, has no visible outline -- use the shorthand, as .printFailed does.)

   No :hover rule of its own is needed, and adding one back would be dead weight: the
   base wash lives on .notificationItemAction:hover, which is the same specificity as
   this rule and appears EARLIER in the file, so `background` here wins on source order
   alone -- and it is !important besides. The status simply does not react to the
   pointer. Keep the background declaration: deleting it is what would let the wash
   through. */
.notificationItemAction.notificationDeducted{
  display:inline-flex;
  align-items:center;
  gap:4px;
  border:1px solid color-mix(in srgb,var(--good,#4ade80) 42%,var(--line))!important;
  background:color-mix(in srgb,var(--good,#4ade80) 10%,transparent)!important;
  color:var(--muted)!important;
  font-weight:800!important;
  cursor:default!important;
}

/* Resolved notification clears itself: slide aside, then collapse so the list
   closes the gap rather than snapping. --exit-h is set from the live height. */
@keyframes notificationSlideOut{
  0%{   transform:translateX(0);      opacity:1; max-height:var(--exit-h,220px); }
  55%{  transform:translateX(118%);   opacity:0; max-height:var(--exit-h,220px); }
  100%{ transform:translateX(118%);   opacity:0; max-height:0;
        margin-top:0; margin-bottom:0; padding-top:0; padding-bottom:0; border-width:0; }
}
.notificationItem.notificationItemExiting{
  animation:notificationSlideOut .6s cubic-bezier(.4,.02,.2,1) forwards;
  overflow:hidden;
  pointer-events:none;
  will-change:transform,opacity,max-height;
}
@media(prefers-reduced-motion:reduce){
  .notificationItem.notificationItemExiting{ animation:none; display:none; }
}
