#include <bits/stdc++.h>
using namespace std;
long long int fact[1016027 + 2];
vector<vector<int> > g;
void fill(int n) {
  long long int i = 2;
  fact[1] = 1;
  fact[0] = 1;
  while (i <= n) {
    fact[i] = (fact[i - 1] * i) % 1000000007;
    i++;
  }
}
long long int poww(int a, int b) {
  long long int x, y;
  y = a;
  x = 1;
  while (b) {
    if (b % 2 == 1) {
      x = (x * y) % 1000000007;
    }
    y = (y * y) % 1000000007;
    b /= 2;
  }
  return x;
}
int compare(int i1, int i2) {
  vector<int>::iterator r1;
  vector<int>::iterator r2;
  int siz1, siz2;
  siz1 = g[i1].size();
  siz2 = g[i2].size();
  int siz = min(siz1, siz2);
  for (int i = 0; i < siz; i++) {
    if (g[i1][i] == g[i2][i]) {
      continue;
    }
    if (g[i1][i] < g[i2][i]) {
      return -1;
    } else {
      return 1;
    }
  }
  if (siz1 == siz2) {
    return 0;
  }
  if (siz1 > siz2) {
    return 1;
  }
  return -1;
}
void merge(int l1, int r1, int l2, int r2) {
  int op_res, counter2, counter1, counter, i;
  counter1 = l1;
  counter2 = l2;
  counter = 0;
  vector<vector<int> > temp;
  temp.resize(r2 - l1 + 3);
  while (counter1 <= r1 && counter2 <= r2) {
    op_res = compare(counter1, counter2);
    if (op_res == 0 || op_res == -1) {
      temp[counter].assign(g[counter1].begin(), g[counter1].end());
      counter1++;
    } else {
      temp[counter].assign(g[counter2].begin(), g[counter2].end());
      counter2++;
    }
    counter++;
  }
  if (counter1 <= r1 || counter2 <= r2) {
    if (counter1 <= r1) {
      while (counter1 <= r1) {
        temp[counter].assign(g[counter1].begin(), g[counter1].end());
        counter1++;
        counter++;
      }
    } else {
      while (counter2 <= r2) {
        temp[counter].assign(g[counter2].begin(), g[counter2].end());
        counter2++;
        counter++;
      }
    }
  }
  for (i = 0; i < (r2 - l1 + 1); i++) {
    g[l1 + i].assign(temp[i].begin(), temp[i].end());
    temp[i].clear();
  }
}
void mergesort(int l, int r) {
  if (l >= r) {
    return;
  }
  int mid = (l + r) / 2;
  mergesort(l, mid);
  mergesort(mid + 1, r);
  merge(l, mid, mid + 1, r);
}
int main() {
  int n, m, i, gi, t;
  cin >> n >> m;
  fill(m + 2);
  g.resize(m + 5);
  for (i = 0; i < n; i++) {
    cin >> gi;
    while (gi--) {
      cin >> t;
      g[t].push_back(i);
    }
  }
  for (i = 1; i <= m; i++) {
    sort(g[i].begin(), g[i].end());
  }
  sort(g.begin() + 1, g.end() - 4);
  int p = 1;
  long long int ans = 1;
  for (i = 2; i <= m; i++) {
    if (g[i] == g[i - 1]) {
      p++;
      continue;
    }
    ans = (ans * fact[p]) % 1000000007;
    p = 1;
  }
  ans = (ans * fact[p]) % 1000000007;
  cout << ans << "\n";
}
