device-state.jsjavascript
/**
 * @file Why saving is blocked, in the user's language — the message table behind
 * requireCanSave(). Extracted like support-form.js so the wording (which is product, not
 * style) is unit-tested in plain Node (test/device-state.test.js).
 *
 * Loaded two ways (UMD-lite): the Electron renderer loads it with a script tag, which
 * attaches window.DeviceState; Node tests require('../src/device-state.js').
 * @module device-state
 */
(function (root, factory) {
  if (typeof module === 'object' && module.exports) module.exports = factory();
  else root.DeviceState = factory();
})(typeof self !== 'undefined' ? self : this, function () {

  /**
   * The one save-blocked question, answered per state. The expired branch uses the same
   * copy as the launch-gate expired notice — one sentence everywhere — and picks "free
   * trial" vs "subscription" off trial_ends_at, which is only present for subs that
   * started as trials.
   * @param {?Object} entitlement Verified entitlement payload, or null when signed out.
   * @param {?{can_save: boolean}} [entitlement.features] Feature flags; can_save true
   *   means saving is allowed.
   * @param {?{authorized: boolean, used: number, max: number}} [entitlement.device]
   *   Device-cap state; authorized false means this machine is over the seat cap.
   * @param {?string} [entitlement.trial_ends_at] Trial-end timestamp when the sub began
   *   as a trial.
   * @returns {?string} Null when saving is allowed, otherwise the user-facing message.
   */
  function saveBlockedMessage(entitlement) {
    if (entitlement?.features?.can_save) return null;
    if (!entitlement) return 'Sign in to save laybacks.';
    if (entitlement.device && entitlement.device.authorized === false) {
      const { used, max } = entitlement.device;
      return `You’re on ${used} of ${max} machines — deactivate one on your account page to save from this one. Preview still works.`;
    }
    const what = entitlement.trial_ends_at ? 'free trial' : 'subscription';
    return `Your ${what} has ended — saving laybacks is disabled.`;
  }

  return { saveBlockedMessage };
});