#include<bits/stdc++.h>
#define XX first
#define YY second
#define pb emplace_back
#define FOR(i,a,b) for(int (i)=(a);i<(b);++(i))
#define EFOR(i,a,b) for(int (i)=(a);i<=(b);++(i))
#define rep(X,Y) for (int (X) = 0;(X) < (Y);++(X))
#define REP rep
#define rrep(X,Y) for (int (X) = (Y)-1;(X) >=0;--(X))
#define all(X) (X).begin(),(X).end()
#define eb emplace_back

using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef ll LL;
typedef pii PII;
typedef pll PLL;
const ll MOD=1e9+7;

#define rall(X) (X).rbegin(),(X).rend()
#define UNIQUE(X) (X).erase(unique(all(X)),(X).end())
#define reps(X,S,Y) for (int (X) = S;(X) < (Y);++(X))
#define rreps(X,S,Y) for (int (X) = (Y)-1;(X) >= (S);--(X))
template<class T> inline bool MX(T &l,const T &r){return l<r?l=r,1:0;}
template<class T> inline bool MN(T &l,const T &r){return l>r?l=r,1:0;}

template<ll mod>
struct ModInt{
  using M=ModInt;
  ll a;
  M& put(ll v){
    a=(v<mod)?v:v-mod;
    return *this;
  }
  ModInt(ll v=0){put(v%mod+mod);}
  M operator+(M x){return M().put(a+x.a);}
  M operator-(M x){return M().put(a+mod-x.a);}
  M operator*(M x){return M().put(a*x.a%mod);}
  M operator/(M x){return x.inv()*a;}
  M& operator+=(M x){return *this=*this+x;};
  M& operator-=(M x){return *this=*this-x;};
  M& operator*=(M x){return *this=*this*x;};
  M& operator/=(M x){return *this=*this/x;};
  bool operator==(M x){return a==x.a;}
  M pow(ll m) {
    M x=*this,res=1;
    while(m){
      if(m&1)res*=x;
      x*=x;
      m>>=1;
    }
    return res;
  }
  M inv(){return pow(mod-2);}
};
using mint = ModInt<998244353>;

int H;
int W;
int hlim;
int wlim;
int rowb[14];
int colb[14];
int field[14][14];
mint fact[100];
mint tmp[14][14][2];
vector<vector<mint>> dp;
vector<vector<mint>> WB;
vector<vector<mint>> BW;

mint Comb(int n, int k) {
  assert(0 <= k && k <= n);
  return fact[n] / fact[k] / fact[n-k];
}

// Zeta Transform g(T)=\sum_{S \subset T}f(S)
template<class T, class F>
inline void FZT(vector<T>&f,F add){
  int n=__lg(f.size());
  int lim = 1 << n;
  rep(i, n) {
    rep(j, lim) {
      if (~j >> i & 1) f[j|1<<i]=add(f[j|1<<i],f[j]);
    }
  }
}
// Modoebius Transform g(T)=\sum_{S \subset T}(-1)^{|T\S|}f(S)
template<class T,class F>
inline void FMT(vector<T>&f,F sub){
  int n=__lg(f.size());
  int lim = 1 << n;
  rep(i, n) {
    rep(j, lim) {
      if (~j >> i & 1) f[j|1<<i]=sub(f[j|1<<i],f[j]);
    }
  }
}
// O(nlog^2n) f*g(S)=\sum_{U∪V=S, U∩V=\emptyset } f(U)*g(V)
template<class T,class F>
inline vector<vector<T>> RankedFZT(vector<T>&f,F add){
  int n=__lg(f.size());
  int lim = 1 << n;
  vector<vector<T>> ret(n+1, vector<T>(f.size()));
  rep(i, lim) ret[__builtin_popcount(i)][i] = f[i];
  rep(i, n+1) FZT(ret[i], add);
  return ret;
}
template<class T,class F, class G>
inline vector<T> ExactConv(vector<T>&f, vector<T>&g, F add, G sub) {
  int n=__lg(f.size());
  int lim = 1 << n;
  auto rf = RankedFZT(f, add);
  auto rg = RankedFZT(g, add);
  vector<vector<T>> c(n+1, vector<T>(f.size()));
  rep(k, n+1) {
    rep(j, k+1) rep(st, lim) {
      c[k][st] += rf[j][st]*rg[k-j][st];
    }
    FZT(c[k], sub);
  }
  vector<T> ret(lim);
  rep(i, lim) ret[i] = c[__builtin_popcount(i)][i];
  return ret;
}

signed main(){
  ios_base::sync_with_stdio(false);
  cout<<fixed<<setprecision(0);

  fact[0] = 1;
  reps(i, 1, 100) {
    fact[i] = fact[i-1] * i;
  }

  cin >> H >> W;
  hlim = 1 << H;
  wlim = 1 << W;
  rep(i, H) {
    rep(j, W) {
      char c;
      cin >> c;
      if (c == '#') field[i][j] = 1;
      else if (c == '.') field[i][j] = 0;
      else assert(0);
    }
  }
  rep(i, H) {
    rrep(j, W) rowb[i] = (rowb[i] << 1) | field[i][j];
  }
  rep(j, W) {
    rrep(i, H) colb[j] = (colb[j] << 1) | field[i][j];
  }

  tmp[0][0][0] = 1;
  tmp[0][0][1] = 1;
  rep(i, H+1) {
    rep(j, W+1) {
      reps(k, 1, H+1) {
        if (i+k > H) break;
        tmp[i+k][j][1] += tmp[i][j][0] * Comb(i+k, k);
      }
      reps(k, 1, W+1) {
        if (j+k > W) break;
        tmp[i][j+k][0] += tmp[i][j][1] * Comb(j+k, k);
      }
    }
  }
  dp.resize(hlim, vector<mint>(wlim));
  rep(i, H+1) {
    rep(j, W+1) {
      dp[i][j] = tmp[i][j][0] + tmp[i][j][1];
    }
  }
  dp[0][0] = 1;
  
  vector<vector<mint>> conv(H+1, vector<mint>(W+1));
  rep(s, H+1) {
    rep(t, W+1) {
      rep(a, s+1) {
        rep(b, t+1) {
          conv[s][t] += Comb(s, a) * Comb(t, b) * dp[a][b] * dp[s-a][t-b];
        }
      }
    }
  }

  mint ans = 0;
  rep(hst, hlim) {
    rep(wst, wlim) {
      bool ng = 0;
      int hexc = ~hst & (hlim-1);
      int wexc = ~wst & (wlim-1);
      rep(j, W) {
        if (wst >> j & 1) continue;
        if ((colb[j] & hexc) == hexc) ng = 1;
        if ((~colb[j] & hexc) == hexc) ng = 1;
      }
      rep(i, H) {
        if (hst >> i & 1) continue;
        if ((rowb[i] & wexc) == wexc) ng = 1;
        if ((~rowb[i] & wexc) == wexc) ng = 1;
      }
      int hcnt = __builtin_popcount(hst);
      int wcnt = __builtin_popcount(wst);
      if (hcnt == H && wcnt == W) ans += dp[H][W];
      else if (!ng) ans += conv[hcnt][wcnt];
    }
  }
  cout << ans.a << endl; 
}


Measured memory_bytes: 8448